Merge "Don't update persistent msgs when dozing"
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 8237383..e9ce87f 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -3331,7 +3331,7 @@
mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
mMaintenanceStartTime = 0;
mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
- cancelLightAlarmLocked();
+ cancelAllLightAlarmsLocked();
}
@GuardedBy("this")
@@ -3406,7 +3406,7 @@
mLightState == LIGHT_STATE_IDLE
|| mLightState == LIGHT_STATE_WAITING_FOR_NETWORK;
} else {
- Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state");
+ Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state: " + mLightState);
return;
}
@@ -3441,7 +3441,7 @@
// connectivity... let's try to wait until the network comes back.
// We'll only wait for another full idle period, however, and then give up.
scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
- mNextLightAlarmTime = 0;
+ cancelLightAlarmLocked();
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3467,7 +3467,7 @@
// We're entering IDLE. We may have used less than curLightIdleBudget for the
// maintenance window, so reschedule the alarm starting from now.
scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
- mNextLightAlarmTime = 0;
+ cancelLightAlarmLocked();
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
mLightState = LIGHT_STATE_IDLE;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3609,7 +3609,7 @@
moveToStateLocked(STATE_IDLE, reason);
if (mLightState != LIGHT_STATE_OVERRIDE) {
mLightState = LIGHT_STATE_OVERRIDE;
- cancelLightAlarmLocked();
+ cancelAllLightAlarmsLocked();
}
addEvent(EVENT_DEEP_IDLE, null);
mGoingIdleWakeLock.acquire();
@@ -3950,11 +3950,21 @@
}
@GuardedBy("this")
- void cancelLightAlarmLocked() {
+ private void cancelAllLightAlarmsLocked() {
+ cancelLightAlarmLocked();
+ cancelLightMaintenanceAlarmLocked();
+ }
+
+ @GuardedBy("this")
+ private void cancelLightAlarmLocked() {
if (mNextLightAlarmTime != 0) {
mNextLightAlarmTime = 0;
mAlarmManager.cancel(mLightAlarmListener);
}
+ }
+
+ @GuardedBy("this")
+ private void cancelLightMaintenanceAlarmLocked() {
if (mNextLightMaintenanceAlarmTime != 0) {
mNextLightMaintenanceAlarmTime = 0;
mAlarmManager.cancel(mLightMaintenanceAlarmListener);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 48f8581..2b0a833 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -2171,7 +2171,7 @@
}
scheduleCutoff();
} else if (mRegularJobTimer && priorityLowered) {
- scheduleCutoff();
+ rescheduleCutoff();
}
}
}
@@ -2207,7 +2207,7 @@
mRunningBgJobs.valueAt(i).getEffectivePriority());
}
if (mLowestPriority != oldPriority) {
- scheduleCutoff();
+ rescheduleCutoff();
}
}
}
@@ -2707,7 +2707,7 @@
if (DEBUG) {
Slog.d(TAG, pkg + " had early REACHED_QUOTA message");
}
- mPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
+ mPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
}
break;
}
@@ -2729,7 +2729,7 @@
if (DEBUG) {
Slog.d(TAG, pkg + " had early REACHED_EJ_QUOTA message");
}
- mEJPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
+ mEJPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
}
break;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index a6a007f..57a9d3b 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -57,7 +57,6 @@
import libcore.util.EmptyArray;
-import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -104,54 +103,6 @@
@GuardedBy("mLock")
private final BalanceThresholdAlarmQueue mBalanceThresholdAlarmQueue;
- /**
- * Comparator to use to sort apps before we distribute ARCs so that we try to give the most
- * important apps ARCs first.
- */
- @VisibleForTesting
- final Comparator<PackageInfo> mPackageDistributionComparator =
- new Comparator<PackageInfo>() {
- @Override
- public int compare(PackageInfo pi1, PackageInfo pi2) {
- final ApplicationInfo appInfo1 = pi1.applicationInfo;
- final ApplicationInfo appInfo2 = pi2.applicationInfo;
- // Put any packages that don't declare an application at the end. A missing
- // <application> tag likely means the app won't be doing any work anyway.
- if (appInfo1 == null) {
- if (appInfo2 == null) {
- return 0;
- }
- return 1;
- } else if (appInfo2 == null) {
- return -1;
- }
- // Privileged apps eat first. They're likely required for the device to
- // function properly.
- // TODO: include headless system apps
- if (appInfo1.isPrivilegedApp()) {
- if (!appInfo2.isPrivilegedApp()) {
- return -1;
- }
- } else if (appInfo2.isPrivilegedApp()) {
- return 1;
- }
-
- // Sort by most recently used.
- final long timeSinceLastUsedMs1 =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(
- pi1.packageName, UserHandle.getUserId(pi1.applicationInfo.uid));
- final long timeSinceLastUsedMs2 =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(
- pi2.packageName, UserHandle.getUserId(pi2.applicationInfo.uid));
- if (timeSinceLastUsedMs1 < timeSinceLastUsedMs2) {
- return -1;
- } else if (timeSinceLastUsedMs1 > timeSinceLastUsedMs2) {
- return 1;
- }
- return 0;
- }
- };
-
private static final int MSG_CHECK_BALANCE = 0;
Agent(@NonNull InternalResourceService irs, @NonNull Scribe scribe) {
@@ -668,7 +619,6 @@
@GuardedBy("mLock")
void distributeBasicIncomeLocked(int batteryLevel) {
List<PackageInfo> pkgs = mIrs.getInstalledPackages();
- pkgs.sort(mPackageDistributionComparator);
final long now = getCurrentTimeMillis();
for (int i = 0; i < pkgs.size(); ++i) {
@@ -709,8 +659,6 @@
mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
final long now = getCurrentTimeMillis();
- pkgs.sort(mPackageDistributionComparator);
-
for (int i = 0; i < pkgs.size(); ++i) {
final PackageInfo packageInfo = pkgs.get(i);
if (!shouldGiveCredits(packageInfo)) {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9564dde..c5410a0 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -189,6 +189,8 @@
instrument.abi = nextArgRequired();
} else if (opt.equals("--no-restart")) {
instrument.noRestart = true;
+ } else if (opt.equals("--always-check-signature")) {
+ instrument.alwaysCheckSignature = true;
} else {
System.err.println("Error: Unknown option: " + opt);
return;
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 0b439df..a0562d9 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -16,6 +16,7 @@
package com.android.commands.am;
+import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
@@ -95,6 +96,7 @@
public Bundle args = new Bundle();
// Required
public String componentNameArg;
+ public boolean alwaysCheckSignature = false;
/**
* Construct the instrument command runner.
@@ -519,6 +521,9 @@
if (noRestart) {
flags |= INSTR_FLAG_NO_RESTART;
}
+ if (alwaysCheckSignature) {
+ flags |= INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
+ }
if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index c134822..4f0ede9 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -23,6 +23,7 @@
cc_defaults {
name: "idmap2_defaults",
+ cpp_std: "gnu++2b",
tidy: true,
tidy_checks: [
"modernize-*",
@@ -31,6 +32,7 @@
"android-*",
"misc-*",
"readability-*",
+ "-readability-identifier-length",
],
tidy_checks_as_errors: [
"modernize-*",
@@ -52,7 +54,6 @@
"-readability-const-return-type",
"-readability-convert-member-functions-to-static",
"-readability-else-after-return",
- "-readability-identifier-length",
"-readability-named-parameter",
"-readability-redundant-access-specifiers",
"-readability-uppercase-literal-suffix",
@@ -113,6 +114,7 @@
"libidmap2/proto/*.proto",
],
host_supported: true,
+ tidy: false,
proto: {
type: "lite",
export_proto_headers: true,
diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp
index 2707049..dad09a0 100644
--- a/cmds/idmap2/idmap2d/Main.cpp
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -15,14 +15,14 @@
*/
#define ATRACE_TAG ATRACE_TAG_RESOURCES
+#define LOG_TAG "idmap2d"
+#include "android-base/logging.h"
#include <binder/BinderService.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <cstdlib> // EXIT_{FAILURE,SUCCESS}
-#include <iostream>
-#include <sstream>
#include "Idmap2Service.h"
#include "android-base/macros.h"
@@ -35,14 +35,17 @@
using android::os::Idmap2Service;
int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) {
+ LOG(INFO) << "Starting";
IPCThreadState::disableBackgroundScheduling(true);
status_t ret = BinderService<Idmap2Service>::publish();
if (ret != android::OK) {
+ LOG(ERROR) << "Failed to start: " << ret;
return EXIT_FAILURE;
}
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
IPCThreadState::self()->joinThreadPool();
+ LOG(INFO) << "Exiting";
return EXIT_SUCCESS;
}
diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp
index f2f8854..f9c4fa3 100644
--- a/cmds/idmap2/tests/ResultTests.cpp
+++ b/cmds/idmap2/tests/ResultTests.cpp
@@ -259,7 +259,8 @@
}
struct NoCopyContainer {
- uint32_t value; // NOLINT(misc-non-private-member-variables-in-classes)
+ uint32_t value = 0; // NOLINT(misc-non-private-member-variables-in-classes)
+ NoCopyContainer() = default;
NoCopyContainer(const NoCopyContainer&) = delete;
NoCopyContainer& operator=(const NoCopyContainer&) = delete;
};
@@ -268,7 +269,7 @@
if (!succeed) {
return Error("foo");
}
- std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U});
+ std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{});
p->value = 42U;
return std::move(p);
}
diff --git a/core/api/current.txt b/core/api/current.txt
index c8d99d5..5b1f59a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17126,6 +17126,9 @@
public final class SensorPrivacyManager {
method public boolean supportsSensorToggle(int);
+ method public boolean supportsSensorToggle(int, int);
+ field public static final int TOGGLE_TYPE_HARDWARE = 2; // 0x2
+ field public static final int TOGGLE_TYPE_SOFTWARE = 1; // 0x1
}
public static class SensorPrivacyManager.Sensors {
@@ -39570,12 +39573,21 @@
field public static final String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE";
field public static final String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
field public static final String DETAILS_META_DATA = "android.speech.DETAILS";
- field public static final String EXTRA_AUDIO_INJECT_SOURCE = "android.speech.extra.AUDIO_INJECT_SOURCE";
+ field @Deprecated public static final String EXTRA_AUDIO_INJECT_SOURCE = "android.speech.extra.AUDIO_INJECT_SOURCE";
+ field public static final String EXTRA_AUDIO_SOURCE = "android.speech.extra.AUDIO_SOURCE";
+ field public static final String EXTRA_AUDIO_SOURCE_CHANNEL_COUNT = "android.speech.extra.AUDIO_SOURCE_CHANNEL_COUNT";
+ field public static final String EXTRA_AUDIO_SOURCE_ENCODING = "android.speech.extra.AUDIO_SOURCE_ENCODING";
+ field public static final String EXTRA_AUDIO_SOURCE_SAMPLING_RATE = "android.speech.extra.AUDIO_SOURCE_SAMPLING_RATE";
+ field public static final String EXTRA_BIASING_STRINGS = "android.speech.extra.BIASING_STRINGS";
field public static final String EXTRA_CALLING_PACKAGE = "calling_package";
field public static final String EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES";
+ field public static final String EXTRA_ENABLE_BIASING_DEVICE_CONTEXT = "android.speech.extra.ENABLE_BIASING_DEVICE_CONTEXT";
+ field public static final String EXTRA_ENABLE_FORMATTING = "android.speech.extra.ENABLE_FORMATTING";
+ field public static final String EXTRA_HIDE_PARTIAL_TRAILING_PUNCTUATION = "android.speech.extra.HIDE_PARTIAL_TRAILING_PUNCTUATION";
field public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
field public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL";
field public static final String EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE";
+ field public static final String EXTRA_MASK_OFFENSIVE_WORDS = "android.speech.extra.MASK_OFFENSIVE_WORDS";
field public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
field public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE";
field public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
@@ -39586,12 +39598,14 @@
field public static final String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
field public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
field public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
- field public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
+ field public static final String EXTRA_SEGMENTED_SESSION = "android.speech.extra.SEGMENTED_SESSION";
field public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
field public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
field public static final String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
field public static final String EXTRA_SUPPORTED_LANGUAGES = "android.speech.extra.SUPPORTED_LANGUAGES";
field public static final String EXTRA_WEB_SEARCH_ONLY = "android.speech.extra.WEB_SEARCH_ONLY";
+ field public static final String FORMATTING_OPTIMIZE_LATENCY = "latency";
+ field public static final String FORMATTING_OPTIMIZE_QUALITY = "quality";
field public static final String LANGUAGE_MODEL_FREE_FORM = "free_form";
field public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search";
field public static final int RESULT_AUDIO_ERROR = 5; // 0x5
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 67091d8..8d9c5db 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2794,7 +2794,7 @@
method public void addActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener, @NonNull java.util.concurrent.Executor);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
- method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @NonNull java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -3759,13 +3759,25 @@
public final class SensorPrivacyManager {
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean areAnySensorPrivacyTogglesEnabled(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int, int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
}
public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
- method public void onSensorPrivacyChanged(int, boolean);
+ method public default void onSensorPrivacyChanged(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams);
+ method @Deprecated public void onSensorPrivacyChanged(int, boolean);
+ }
+
+ public static class SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams {
+ method public int getSensor();
+ method public int getToggleType();
+ method public boolean isEnabled();
}
}
@@ -10032,6 +10044,21 @@
field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
}
+ public final class PermissionGroupUsage implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public CharSequence getAttributionLabel();
+ method @Nullable public CharSequence getAttributionTag();
+ method public long getLastAccessTimeMillis();
+ method @NonNull public String getPackageName();
+ method @NonNull public String getPermissionGroupName();
+ method @Nullable public CharSequence getProxyLabel();
+ method public int getUid();
+ method public boolean isActive();
+ method public boolean isPhoneCall();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.permission.PermissionGroupUsage> CREATOR;
+ }
+
public final class PermissionManager {
method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int);
method public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
@@ -10048,6 +10075,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, long, int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
field @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS";
+ field public static final String EXTRA_PERMISSION_USAGES = "android.permission.extra.PERMISSION_USAGES";
field public static final int PERMISSION_GRANTED = 0; // 0x0
field public static final int PERMISSION_HARD_DENIED = 2; // 0x2
field public static final int PERMISSION_SOFT_DENIED = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e5165449..29b8248 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2058,17 +2058,6 @@
package android.permission {
- public final class PermGroupUsage {
- ctor public PermGroupUsage(@NonNull String, int, @NonNull String, long, boolean, boolean, @Nullable CharSequence);
- method @Nullable public CharSequence getAttribution();
- method public long getLastAccess();
- method @NonNull public String getPackageName();
- method @NonNull public String getPermGroupName();
- method public int getUid();
- method public boolean isActive();
- method public boolean isPhoneCall();
- }
-
public final class PermissionControllerManager {
method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
@@ -2084,8 +2073,8 @@
}
public final class PermissionManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
- method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getIndicatorAppOpUsageData();
+ method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getIndicatorAppOpUsageData(boolean);
method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
method @RequiresPermission(android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL) public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 89dd9ef..9f15105 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -179,6 +179,12 @@
* @hide
*/
public static final int INSTR_FLAG_NO_RESTART = 1 << 3;
+ /**
+ * Force the check that instrumentation and the target package are signed with the same
+ * certificate even if {@link Build#IS_DEBUGGABLE} is {@code true}.
+ * @hide
+ */
+ public static final int INSTR_FLAG_ALWAYS_CHECK_SIGNATURE = 1 << 4;
static final class UidObserver extends IUidObserver.Stub {
final OnUidImportanceListener mListener;
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 99ce147..02d140f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -48,7 +48,6 @@
import android.util.ArrayMap;
import android.view.Surface;
-import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -223,7 +222,8 @@
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
* VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
* @param executor The executor on which {@code callback} will be invoked. This is ignored
- * if {@code callback} is {@code null}.
+ * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
+ * not be null.
* @param callback Callback to call when the state of the {@link VirtualDisplay} changes
* @return The newly created virtual display, or {@code null} if the application could
* not create the virtual display.
@@ -237,7 +237,7 @@
@IntRange(from = 1) int densityDpi,
@Nullable Surface surface,
@VirtualDisplayFlag int flags,
- @NonNull @CallbackExecutor Executor executor,
+ @Nullable @CallbackExecutor Executor executor,
@Nullable VirtualDisplay.Callback callback) {
// TODO(b/205343547): Handle display groups properly instead of creating a new display
// group for every new virtual display created using this API.
@@ -253,7 +253,7 @@
.setFlags(getVirtualDisplayFlags(flags))
.build(),
callback,
- Objects.requireNonNull(executor));
+ executor);
}
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index be58ba7..fbb03ca 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,6 +32,7 @@
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.util.ArrayMap;
@@ -1865,7 +1866,7 @@
}
public ApplicationInfo() {
- createTimestamp = System.currentTimeMillis();
+ createTimestamp = SystemClock.uptimeMillis();
}
public ApplicationInfo(ApplicationInfo orig) {
@@ -1939,7 +1940,7 @@
nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
localeConfigRes = orig.localeConfigRes;
- createTimestamp = System.currentTimeMillis();
+ createTimestamp = SystemClock.uptimeMillis();
}
public String toString() {
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 4399af9..a3cc01c 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -158,36 +158,30 @@
}
+
+ /**
+ * Constant for software toggle.
+ */
+ public static final int TOGGLE_TYPE_SOFTWARE =
+ SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
+
+ /**
+ * Constant for hardware toggle.
+ */
+ public static final int TOGGLE_TYPE_HARDWARE =
+ SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
+
/**
* Types of toggles which can exist for sensor privacy
+ *
* @hide
*/
- public static class ToggleTypes {
- private ToggleTypes() {}
-
- /**
- * Constant for software toggle.
- */
- public static final int SOFTWARE = SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
-
- /**
- * Constant for hardware toggle.
- */
- public static final int HARDWARE = SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
-
- /**
- * Types of toggles which can exist for sensor privacy
- *
- * @hide
- */
- @IntDef(value = {
- SOFTWARE,
- HARDWARE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ToggleType {}
-
- }
+ @IntDef(value = {
+ TOGGLE_TYPE_SOFTWARE,
+ TOGGLE_TYPE_HARDWARE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ToggleType {}
/**
* Types of state which can exist for the sensor privacy toggle
@@ -232,20 +226,23 @@
/**
* Callback invoked when the sensor privacy state changes.
*
+ * @param params Parameters describing the new state
+ */
+ default void onSensorPrivacyChanged(@NonNull SensorPrivacyChangedParams params) {
+ onSensorPrivacyChanged(params.mSensor, params.mEnabled);
+ }
+
+ /**
+ * Callback invoked when the sensor privacy state changes.
+ *
* @param sensor the sensor whose state is changing
* @param enabled true if sensor privacy is enabled, false otherwise.
+ *
+ * @deprecated Please use
+ * {@link #onSensorPrivacyChanged(SensorPrivacyChangedParams)}
*/
+ @Deprecated
void onSensorPrivacyChanged(int sensor, boolean enabled);
- }
-
- /**
- * A class implementing this interface can register with the {@link
- * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
- * state changes.
- *
- * @hide
- */
- public interface OnToggleSensorPrivacyChangedListener {
/**
* A class containing information about what the sensor privacy state has changed to.
@@ -262,7 +259,7 @@
mEnabled = enabled;
}
- public @ToggleTypes.ToggleType int getToggleType() {
+ public @ToggleType int getToggleType() {
return mToggleType;
}
@@ -274,13 +271,6 @@
return mEnabled;
}
}
-
- /**
- * Callback invoked when the sensor privacy state changes.
- *
- * @param params Parameters describing the new state
- */
- void onSensorPrivacyChanged(@NonNull SensorPrivacyChangedParams params);
}
private static final Object sInstanceLock = new Object();
@@ -305,15 +295,15 @@
/** Registered listeners */
@GuardedBy("mLock")
@NonNull
- private final ArrayMap<OnToggleSensorPrivacyChangedListener, Executor> mToggleListeners =
+ private final ArrayMap<OnSensorPrivacyChangedListener, Executor> mToggleListeners =
new ArrayMap<>();
/** Listeners registered using the deprecated APIs and which
- * OnToggleSensorPrivacyChangedListener they're using. */
+ * OnSensorPrivacyChangedListener they're using. */
@GuardedBy("mLock")
@NonNull
private final ArrayMap<Pair<Integer, OnSensorPrivacyChangedListener>,
- OnToggleSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
+ OnSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
/** The singleton ISensorPrivacyListener for IPC which will be used to dispatch to local
* listeners */
@@ -323,9 +313,9 @@
public void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled) {
synchronized (mLock) {
for (int i = 0; i < mToggleListeners.size(); i++) {
- OnToggleSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
+ OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
mToggleListeners.valueAt(i).execute(() -> listener
- .onSensorPrivacyChanged(new OnToggleSensorPrivacyChangedListener
+ .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener
.SensorPrivacyChangedParams(toggleType, sensor, enabled)));
}
}
@@ -367,12 +357,24 @@
}
/**
+ * Returns the single instance of the SensorPrivacyManager.
+ *
+ * @hide
+ */
+ public static SensorPrivacyManager getInstance(Context context, ISensorPrivacyManager service) {
+ synchronized (sInstanceLock) {
+ sInstance = new SensorPrivacyManager(context, service);
+ return sInstance;
+ }
+ }
+
+ /**
* Checks if the given toggle is supported on this device
* @param sensor The sensor to check
* @return whether the toggle for the sensor is supported on this device.
*/
public boolean supportsSensorToggle(@Sensors.Sensor int sensor) {
- return supportsSensorToggle(ToggleTypes.SOFTWARE, sensor);
+ return supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor);
}
/**
@@ -380,10 +382,8 @@
* @param sensor The sensor to check
* @return whether the toggle for the sensor is supported on this device.
*
- * @hide
*/
- public boolean supportsSensorToggle(@ToggleTypes.ToggleType int toggleType,
- @Sensors.Sensor int sensor) {
+ public boolean supportsSensorToggle(@ToggleType int toggleType, @Sensors.Sensor int sensor) {
try {
Pair key = new Pair(toggleType, sensor);
synchronized (mLock) {
@@ -408,8 +408,6 @@
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*
- * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)}
- *
* @hide
*/
@SystemApi
@@ -429,8 +427,6 @@
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*
- * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)}
- *
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
@@ -449,19 +445,22 @@
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*
- * {@link #addSensorPrivacyListener(Executor, OnToggleSensorPrivacyChangedListener)}
- *
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
@NonNull OnSensorPrivacyChangedListener listener) {
-
Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
- OnToggleSensorPrivacyChangedListener toggleListener = params -> {
- if (params.getSensor() == sensor) {
- listener.onSensorPrivacyChanged(params.getSensor(), params.isEnabled());
+ OnSensorPrivacyChangedListener toggleListener = new OnSensorPrivacyChangedListener() {
+ @Override
+ public void onSensorPrivacyChanged(SensorPrivacyChangedParams params) {
+ if (params.getSensor() == sensor) {
+ listener.onSensorPrivacyChanged(params.getSensor(), params.isEnabled());
+ }
+ }
+ @Override
+ public void onSensorPrivacyChanged(int sensor, boolean enabled) {
}
};
@@ -476,13 +475,14 @@
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
- * @param listener the OnToggleSensorPrivacyChangedListener to be notified when the state of
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
* sensor privacy changes.
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public void addSensorPrivacyListener(@NonNull OnToggleSensorPrivacyChangedListener listener) {
+ public void addSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(mContext.getMainExecutor(), listener);
}
@@ -492,14 +492,15 @@
* changes.
*
* @param executor the executor to dispatch the callback on
- * @param listener the OnToggleSensorPrivacyChangedListener to be notified when the state of
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
* sensor privacy changes.
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@NonNull Executor executor,
- @NonNull OnToggleSensorPrivacyChangedListener listener) {
+ @NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mLock) {
addSensorPrivacyListenerLocked(executor, listener);
}
@@ -507,7 +508,7 @@
@GuardedBy("mLock")
private void addSensorPrivacyListenerLocked(@NonNull Executor executor,
- @NonNull OnToggleSensorPrivacyChangedListener listener) {
+ @NonNull OnSensorPrivacyChangedListener listener) {
if (!mToggleListenerRegistered) {
try {
mService.addToggleSensorPrivacyListener(mIToggleListener);
@@ -529,10 +530,6 @@
* @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
* sensor privacy changes.
*
- * {@link #removeSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)} with
- * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)} or
- * {@link #addSensorPrivacyListener(Executor, OnToggleSensorPrivacyChangedListener)}
- *
* @hide
*/
@SystemApi
@@ -541,7 +538,7 @@
@NonNull OnSensorPrivacyChangedListener listener) {
Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
synchronized (mLock) {
- OnToggleSensorPrivacyChangedListener onToggleSensorPrivacyChangedListener =
+ OnSensorPrivacyChangedListener onToggleSensorPrivacyChangedListener =
mLegacyToggleListeners.remove(pair);
if (onToggleSensorPrivacyChangedListener != null) {
removeSensorPrivacyListenerLocked(onToggleSensorPrivacyChangedListener);
@@ -553,14 +550,15 @@
* Unregisters the specified listener from receiving notifications when the state of any sensor
* privacy changes.
*
- * @param listener the {@link OnToggleSensorPrivacyChangedListener} to be unregistered from
+ * @param listener the {@link OnSensorPrivacyChangedListener} to be unregistered from
* notifications when sensor privacy changes.
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void removeSensorPrivacyListener(
- @NonNull OnToggleSensorPrivacyChangedListener listener) {
+ @NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mLock) {
removeSensorPrivacyListenerLocked(listener);
}
@@ -568,7 +566,7 @@
@GuardedBy("mLock")
private void removeSensorPrivacyListenerLocked(
- @NonNull OnToggleSensorPrivacyChangedListener listener) {
+ @NonNull OnSensorPrivacyChangedListener listener) {
mToggleListeners.remove(listener);
if (mToggleListeners.size() == 0) {
try {
@@ -586,12 +584,15 @@
*
* @return true if sensor privacy is currently enabled, false otherwise.
*
+ * @deprecated Prefer to use {@link #isSensorPrivacyEnabled(int, int)}
+ *
* @hide
*/
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
- return isSensorPrivacyEnabled(ToggleTypes.SOFTWARE, sensor);
+ return isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor);
}
/**
@@ -601,8 +602,9 @@
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public boolean isSensorPrivacyEnabled(@ToggleTypes.ToggleType int toggleType,
+ public boolean isSensorPrivacyEnabled(@ToggleType int toggleType,
@Sensors.Sensor int sensor) {
try {
return mService.isToggleSensorPrivacyEnabled(toggleType, sensor);
@@ -613,14 +615,16 @@
/**
* Returns whether sensor privacy is currently enabled for a specific sensor.
- * Combines the state of the SW + HW toggles and returns the actual privacy state.
+ * Combines the state of the SW + HW toggles and returns true if either the
+ * SOFTWARE or the HARDWARE toggles are enabled.
*
* @return true if sensor privacy is currently enabled, false otherwise.
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public boolean isToggleSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
+ public boolean areAnySensorPrivacyTogglesEnabled(@Sensors.Sensor int sensor) {
try {
return mService.isCombinedToggleSensorPrivacyEnabled(sensor);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index c053c92..c341731 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -957,7 +957,7 @@
public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
@NonNull VirtualDisplayConfig virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback,
- @NonNull Executor executor) {
+ @Nullable Executor executor) {
return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
virtualDisplayConfig, callback, executor, null);
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 889100d..a62bbf6 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1054,6 +1054,14 @@
@Nullable private final VirtualDisplay.Callback mCallback;
@Nullable private final Executor mExecutor;
+ /**
+ * Creates a virtual display callback.
+ *
+ * @param callback The callback to call for virtual display events, or {@code null} if the
+ * caller does not wish to receive callback events.
+ * @param executor The executor to call the {@code callback} on. Must not be {@code null} if
+ * the callback is not {@code null}.
+ */
VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
mCallback = callback;
mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index bf4b514..eed92c1 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -37,6 +37,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.io.IOException;
+
/**
* The SoundTriggerModule provides APIs to control sound models and sound detection
* on a given sound trigger hardware module.
@@ -137,13 +139,39 @@
if (model instanceof SoundTrigger.GenericSoundModel) {
SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
(SoundTrigger.GenericSoundModel) model);
- soundModelHandle[0] = mService.loadModel(aidlModel);
+ try {
+ soundModelHandle[0] = mService.loadModel(aidlModel);
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a
+ // try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (aidlModel.data != null) {
+ try {
+ aidlModel.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
+ }
return SoundTrigger.STATUS_OK;
}
if (model instanceof SoundTrigger.KeyphraseSoundModel) {
PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
(SoundTrigger.KeyphraseSoundModel) model);
- soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+ try {
+ soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a
+ // try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (aidlModel.common.data != null) {
+ try {
+ aidlModel.common.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
+ }
return SoundTrigger.STATUS_OK;
}
return SoundTrigger.STATUS_BAD_VALUE;
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 6b869f1..043e688 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -106,6 +106,9 @@
/** @hide */
public static final long TRACE_TAG_RRO = 1L << 26;
/** @hide */
+ public static final long TRACE_TAG_THERMAL = 1L << 27;
+ /** @hide */
+
public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 02db274..29b4570 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -19,10 +19,54 @@
/**
* @hide
*/
-interface ILogcatManagerService {
+oneway interface ILogcatManagerService {
+ /**
+ * The function is called by logd to notify LogcatManagerService
+ * that a client makes privileged log data access request.
+ *
+ * @param uid The UID of client who makes the request.
+ * @param gid The GID of client who makes the request.
+ * @param pid The PID of client who makes the request.
+ * @param fd The FD (Socket) of client who makes the request.
+ */
void startThread(in int uid, in int gid, in int pid, in int fd);
+
+
+ /**
+ * The function is called by logd to notify LogcatManagerService
+ * that a client finished the privileged log data access.
+ *
+ * @param uid The UID of client who makes the request.
+ * @param gid The GID of client who makes the request.
+ * @param pid The PID of client who makes the request.
+ * @param fd The FD (Socket) of client who makes the request.
+ */
void finishThread(in int uid, in int gid, in int pid, in int fd);
+
+
+ /**
+ * The function is called by UX component to notify
+ * LogcatManagerService that the user approved
+ * the privileged log data access.
+ *
+ * @param uid The UID of client who makes the request.
+ * @param gid The GID of client who makes the request.
+ * @param pid The PID of client who makes the request.
+ * @param fd The FD (Socket) of client who makes the request.
+ */
void approve(in int uid, in int gid, in int pid, in int fd);
+
+
+ /**
+ * The function is called by UX component to notify
+ * LogcatManagerService that the user declined
+ * the privileged log data access.
+ *
+ * @param uid The UID of client who makes the request.
+ * @param gid The GID of client who makes the request.
+ * @param pid The PID of client who makes the request.
+ * @param fd The FD (Socket) of client who makes the request.
+ */
void decline(in int uid, in int gid, in int pid, in int fd);
}
diff --git a/core/java/android/permission/PermGroupUsage.java b/core/java/android/permission/PermGroupUsage.java
deleted file mode 100644
index 440d6f2..0000000
--- a/core/java/android/permission/PermGroupUsage.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-
-/**
- * Represents the usage of a permission group by an app. Supports package name, user, permission
- * group, whether or not the access is running or recent, whether the access is tied to a phone
- * call, and an optional special attribution.
- *
- * @hide
- */
-@TestApi
-public final class PermGroupUsage {
-
- private final String mPackageName;
- private final int mUid;
- private final long mLastAccess;
- private final String mPermGroupName;
- private final boolean mIsActive;
- private final boolean mIsPhoneCall;
- private final CharSequence mAttribution;
-
- /**
- *
- * @param packageName The package name of the using app
- * @param uid The uid of the using app
- * @param permGroupName The name of the permission group being used
- * @param lastAccess The time of last access
- * @param isActive Whether this is active
- * @param isPhoneCall Whether this is a usage by the phone
- * @param attribution An optional string attribution to show
- * @hide
- */
- @TestApi
- public PermGroupUsage(@NonNull String packageName, int uid,
- @NonNull String permGroupName, long lastAccess, boolean isActive, boolean isPhoneCall,
- @Nullable CharSequence attribution) {
- this.mPackageName = packageName;
- this.mUid = uid;
- this.mPermGroupName = permGroupName;
- this.mLastAccess = lastAccess;
- this.mIsActive = isActive;
- this.mIsPhoneCall = isPhoneCall;
- this.mAttribution = attribution;
- }
-
- /**
- * @hide
- */
- @TestApi
- public @NonNull String getPackageName() {
- return mPackageName;
- }
-
- /**
- * @hide
- */
- @TestApi
- public int getUid() {
- return mUid;
- }
-
- /**
- * @hide
- */
- @TestApi
- public @NonNull String getPermGroupName() {
- return mPermGroupName;
- }
-
- /**
- * @hide
- */
- @TestApi
- public long getLastAccess() {
- return mLastAccess;
- }
-
- /**
- * @hide
- */
- @TestApi
- public boolean isActive() {
- return mIsActive;
- }
-
- /**
- * @hide
- */
- @TestApi
- public boolean isPhoneCall() {
- return mIsPhoneCall;
- }
-
- /**
- * @hide
- */
- @TestApi
- public @Nullable CharSequence getAttribution() {
- return mAttribution;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this))
- + " packageName: " + mPackageName + ", UID: " + mUid + ", permGroup: "
- + mPermGroupName + ", lastAccess: " + mLastAccess + ", isActive: " + mIsActive
- + ", attribution: " + mAttribution;
- }
-}
diff --git a/core/java/android/permission/PermissionGroupUsage.aidl b/core/java/android/permission/PermissionGroupUsage.aidl
new file mode 100644
index 0000000..f18698a
--- /dev/null
+++ b/core/java/android/permission/PermissionGroupUsage.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2022, 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.permission;
+
+parcelable PermissionGroupUsage;
\ No newline at end of file
diff --git a/core/java/android/permission/PermissionGroupUsage.java b/core/java/android/permission/PermissionGroupUsage.java
new file mode 100644
index 0000000..49b7463
--- /dev/null
+++ b/core/java/android/permission/PermissionGroupUsage.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2022 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.permission;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Represents the usage of a permission group by an app. Supports package name, user, permission
+ * group, whether or not the access is running or recent, whether the access is tied to a phone
+ * call, and an optional special attribution tag, label and proxy label.
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(
+ genHiddenConstructor = true,
+ genEqualsHashCode = true,
+ genToString = true
+)
+public final class PermissionGroupUsage implements Parcelable {
+
+ private final @NonNull String mPackageName;
+ private final int mUid;
+ private final long mLastAccessTimeMillis;
+ private final @NonNull String mPermissionGroupName;
+ private final boolean mActive;
+ private final boolean mPhoneCall;
+ private final @Nullable CharSequence mAttributionTag;
+ private final @Nullable CharSequence mAttributionLabel;
+ private final @Nullable CharSequence mProxyLabel;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/permission/PermissionGroupUsage.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new PermissionGroupUsage.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public PermissionGroupUsage(
+ @NonNull String packageName,
+ int uid,
+ long lastAccessTimeMillis,
+ @NonNull String permissionGroupName,
+ boolean active,
+ boolean phoneCall,
+ @Nullable CharSequence attributionTag,
+ @Nullable CharSequence attributionLabel,
+ @Nullable CharSequence proxyLabel) {
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mUid = uid;
+ this.mLastAccessTimeMillis = lastAccessTimeMillis;
+ this.mPermissionGroupName = permissionGroupName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPermissionGroupName);
+ this.mActive = active;
+ this.mPhoneCall = phoneCall;
+ this.mAttributionTag = attributionTag;
+ this.mAttributionLabel = attributionLabel;
+ this.mProxyLabel = proxyLabel;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * @return Package name for the usage
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * @return UID for the usage
+ */
+ @DataClass.Generated.Member
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
+ * @return Last access time in millis for the usage
+ */
+ @CurrentTimeMillisLong
+ @DataClass.Generated.Member
+ public long getLastAccessTimeMillis() {
+ return mLastAccessTimeMillis;
+ }
+
+ /**
+ * @return Permission group name for the usage
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPermissionGroupName() {
+ return mPermissionGroupName;
+ }
+
+ /**
+ * @return If usage is active
+ */
+ @DataClass.Generated.Member
+ public boolean isActive() {
+ return mActive;
+ }
+
+ /**
+ * @return If usage is a phone call
+ */
+ @DataClass.Generated.Member
+ public boolean isPhoneCall() {
+ return mPhoneCall;
+ }
+
+ /**
+ * @return Attribution tag associated with the usage
+ */
+ @DataClass.Generated.Member
+ public @Nullable CharSequence getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ /**
+ * @return Attribution label associated with the usage
+ */
+ @DataClass.Generated.Member
+ public @Nullable CharSequence getAttributionLabel() {
+ return mAttributionLabel;
+ }
+
+ /**
+ * @return Proxy label associated with the usage
+ */
+ @DataClass.Generated.Member
+ public @Nullable CharSequence getProxyLabel() {
+ return mProxyLabel;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "PermissionGroupUsage { " +
+ "packageName = " + mPackageName + ", " +
+ "uid = " + mUid + ", " +
+ "lastAccessTimeMillis = " + mLastAccessTimeMillis + ", " +
+ "permissionGroupName = " + mPermissionGroupName + ", " +
+ "active = " + mActive + ", " +
+ "phoneCall = " + mPhoneCall + ", " +
+ "attributionTag = " + mAttributionTag + ", " +
+ "attributionLabel = " + mAttributionLabel + ", " +
+ "proxyLabel = " + mProxyLabel +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(PermissionGroupUsage other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ PermissionGroupUsage that = (PermissionGroupUsage) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mPackageName, that.mPackageName)
+ && mUid == that.mUid
+ && mLastAccessTimeMillis == that.mLastAccessTimeMillis
+ && java.util.Objects.equals(mPermissionGroupName, that.mPermissionGroupName)
+ && mActive == that.mActive
+ && mPhoneCall == that.mPhoneCall
+ && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+ && java.util.Objects.equals(mAttributionLabel, that.mAttributionLabel)
+ && java.util.Objects.equals(mProxyLabel, that.mProxyLabel);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + mUid;
+ _hash = 31 * _hash + Long.hashCode(mLastAccessTimeMillis);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPermissionGroupName);
+ _hash = 31 * _hash + Boolean.hashCode(mActive);
+ _hash = 31 * _hash + Boolean.hashCode(mPhoneCall);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionLabel);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mProxyLabel);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ int flg = 0;
+ if (mActive) flg |= 0x10;
+ if (mPhoneCall) flg |= 0x20;
+ if (mAttributionTag != null) flg |= 0x40;
+ if (mAttributionLabel != null) flg |= 0x80;
+ if (mProxyLabel != null) flg |= 0x100;
+ dest.writeInt(flg);
+ dest.writeString(mPackageName);
+ dest.writeInt(mUid);
+ dest.writeLong(mLastAccessTimeMillis);
+ dest.writeString(mPermissionGroupName);
+ if (mAttributionTag != null) dest.writeCharSequence(mAttributionTag);
+ if (mAttributionLabel != null) dest.writeCharSequence(mAttributionLabel);
+ if (mProxyLabel != null) dest.writeCharSequence(mProxyLabel);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ PermissionGroupUsage(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int flg = in.readInt();
+ boolean active = (flg & 0x10) != 0;
+ boolean phoneCall = (flg & 0x20) != 0;
+ String packageName = in.readString();
+ int uid = in.readInt();
+ long lastAccessTimeMillis = in.readLong();
+ String permissionGroupName = in.readString();
+ CharSequence attributionTag = (flg & 0x40) == 0 ? null : (CharSequence) in.readCharSequence();
+ CharSequence attributionLabel = (flg & 0x80) == 0 ? null : (CharSequence) in.readCharSequence();
+ CharSequence proxyLabel = (flg & 0x100) == 0 ? null : (CharSequence) in.readCharSequence();
+
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mUid = uid;
+ this.mLastAccessTimeMillis = lastAccessTimeMillis;
+ this.mPermissionGroupName = permissionGroupName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPermissionGroupName);
+ this.mActive = active;
+ this.mPhoneCall = phoneCall;
+ this.mAttributionTag = attributionTag;
+ this.mAttributionLabel = attributionLabel;
+ this.mProxyLabel = proxyLabel;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<PermissionGroupUsage> CREATOR
+ = new Parcelable.Creator<PermissionGroupUsage>() {
+ @Override
+ public PermissionGroupUsage[] newArray(int size) {
+ return new PermissionGroupUsage[size];
+ }
+
+ @Override
+ public PermissionGroupUsage createFromParcel(@NonNull android.os.Parcel in) {
+ return new PermissionGroupUsage(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1645067417023L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/permission/PermissionGroupUsage.java",
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final int mUid\nprivate final long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final boolean mActive\nprivate final boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index fc7ac11..c509de6 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -193,6 +193,17 @@
*/
public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
+ /**
+ * Intent extra: List of PermissionGroupUsages
+ * <p>
+ * Type: {@code List<PermissionGroupUsage>}
+ * </p>
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PERMISSION_USAGES =
+ "android.permission.extra.PERMISSION_USAGES";
+
private final @NonNull Context mContext;
private final IPackageManager mPackageManager;
@@ -1108,7 +1119,7 @@
@TestApi
@NonNull
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
- public List<PermGroupUsage> getIndicatorAppOpUsageData() {
+ public List<PermissionGroupUsage> getIndicatorAppOpUsageData() {
return getIndicatorAppOpUsageData(new AudioManager().isMicrophoneMute());
}
@@ -1122,7 +1133,7 @@
@TestApi
@NonNull
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
- public List<PermGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
+ public List<PermissionGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
// Lazily initialize the usage helper
initializeUsageHelper();
return mUsageHelper.getOpUsageData(micMuted);
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 658e033..0b57842 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -42,7 +42,10 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Attribution;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.icu.text.ListFormatter;
import android.media.AudioManager;
import android.os.Process;
@@ -70,20 +73,30 @@
public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener,
AppOpsManager.OnOpStartedListener {
- /** Whether to show the mic and camera icons. */
+ /**
+ * Whether to show the mic and camera icons.
+ */
private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
- /** Whether to show the location indicators. */
+ /**
+ * Whether to show the location indicators.
+ */
private static final String PROPERTY_LOCATION_INDICATORS_ENABLED =
"location_indicators_enabled";
- /** Whether to show the Permissions Hub. */
+ /**
+ * Whether to show the Permissions Hub.
+ */
private static final String PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled";
- /** How long after an access to show it as "recent" */
+ /**
+ * How long after an access to show it as "recent"
+ */
private static final String RECENT_ACCESS_TIME_MS = "recent_access_time_ms";
- /** How long after an access to show it as "running" */
+ /**
+ * How long after an access to show it as "running"
+ */
private static final String RUNNING_ACCESS_TIME_MS = "running_access_time_ms";
private static final String SYSTEM_PKG = "android";
@@ -132,7 +145,7 @@
);
private static @NonNull String getGroupForOp(String op) {
- switch(op) {
+ switch (op) {
case OPSTR_RECORD_AUDIO:
return MICROPHONE;
case OPSTR_CAMERA:
@@ -158,6 +171,7 @@
/**
* Constructor for PermissionUsageHelper
+ *
* @param context The context from which to derive the package information
*/
public PermissionUsageHelper(@NonNull Context context) {
@@ -167,9 +181,9 @@
mUserContexts = new ArrayMap<>();
mUserContexts.put(Process.myUserHandle(), mContext);
// TODO ntmyren: make this listen for flag enable/disable changes
- String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
+ String[] opStrs = {OPSTR_CAMERA, OPSTR_RECORD_AUDIO};
mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this);
- int[] ops = { OP_CAMERA, OP_RECORD_AUDIO };
+ int[] ops = {OP_CAMERA, OP_RECORD_AUDIO};
mAppOpsManager.startWatchingStarted(ops, this);
}
@@ -225,8 +239,8 @@
@Override
public void onOpStarted(int op, int uid, String packageName, String attributionTag,
- @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
- // not part of an attribution chain. Do nothing
+ @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
+ // not part of an attribution chain. Do nothing
}
@Override
@@ -274,8 +288,8 @@
/**
* @see PermissionManager.getIndicatorAppOpUsageData
*/
- public @NonNull List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
- List<PermGroupUsage> usages = new ArrayList<>();
+ public @NonNull List<PermissionGroupUsage> getOpUsageData(boolean isMicMuted) {
+ List<PermissionGroupUsage> usages = new ArrayList<>();
if (!shouldShowIndicators()) {
return usages;
@@ -313,6 +327,9 @@
}
}
+ // map of package name -> map of attribution tag -> attribution labels
+ ArrayMap<String, Map<String, String>> subAttributionLabelsMap = new ArrayMap<>();
+
for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) {
boolean isPhone = false;
String permGroup = usedPermGroups.get(permGroupNum);
@@ -320,6 +337,8 @@
ArrayMap<OpUsage, CharSequence> usagesWithLabels =
getUniqueUsagesWithLabels(permGroup, rawUsages.get(permGroup));
+ updateSubattributionLabelsMap(rawUsages.get(permGroup), subAttributionLabelsMap);
+
if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
isPhone = true;
permGroup = MICROPHONE;
@@ -330,15 +349,86 @@
for (int usageNum = 0; usageNum < usagesWithLabels.size(); usageNum++) {
OpUsage usage = usagesWithLabels.keyAt(usageNum);
- usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup,
- usage.lastAccessTime, usage.isRunning, isPhone,
- usagesWithLabels.valueAt(usageNum)));
+ String attributionLabel = subAttributionLabelsMap.getOrDefault(usage.packageName,
+ new ArrayMap<>()).getOrDefault(usage.attributionTag, null);
+ usages.add(
+ new PermissionGroupUsage(usage.packageName, usage.uid, usage.lastAccessTime,
+ permGroup,
+ usage.isRunning, isPhone, usage.attributionTag, attributionLabel,
+ usagesWithLabels.valueAt(usageNum)));
}
}
return usages;
}
+ private void updateSubattributionLabelsMap(List<OpUsage> usages,
+ ArrayMap<String, Map<String, String>> subAttributionLabelsMap) {
+ if (usages == null || usages.isEmpty()) {
+ return;
+ }
+ for (OpUsage usage : usages) {
+ if (usage.attributionTag != null && !subAttributionLabelsMap.containsKey(
+ usage.packageName)) {
+ subAttributionLabelsMap.put(usage.packageName,
+ getSubattributionLabelsForPackage(usage.packageName, usage.uid));
+ }
+ }
+ }
+
+ /**
+ * Query attribution labels for a package
+ *
+ * @param packageName
+ * @param uid
+ * @return map of attribution tag -> attribution labels for a package
+ */
+ private ArrayMap<String, String> getSubattributionLabelsForPackage(String packageName,
+ int uid) {
+ ArrayMap<String, String> attributionLabelMap = new ArrayMap<>();
+ UserHandle user = UserHandle.getUserHandleForUid(uid);
+ try {
+ if (!isSubattributionSupported(packageName, uid)) {
+ return attributionLabelMap;
+ }
+ Context userContext = getUserContext(user);
+ PackageInfo packageInfo = userContext.getPackageManager().getPackageInfo(
+ packageName,
+ PackageManager.GET_PERMISSIONS | PackageManager.GET_ATTRIBUTIONS);
+ Context pkgContext = userContext.createPackageContext(packageInfo.packageName, 0);
+ for (Attribution attribution : packageInfo.attributions) {
+ try {
+ String resourceForLabel = pkgContext.getString(attribution.getLabel());
+ attributionLabelMap.put(attribution.getTag(), resourceForLabel);
+ } catch (Resources.NotFoundException e) {
+ // Shouldn't happen, do nothing
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Did not find the package, do nothing
+ }
+ return attributionLabelMap;
+ }
+
+ /**
+ * Returns true if the app supports subattribution.
+ */
+ private boolean isSubattributionSupported(String packageName, int uid) {
+ try {
+ PackageManager userPkgManager =
+ getUserContext(UserHandle.getUserHandleForUid(uid)).getPackageManager();
+ ApplicationInfo appInfo = userPkgManager.getApplicationInfoAsUser(packageName,
+ PackageManager.ApplicationInfoFlags.of(0),
+ UserHandle.getUserId(uid));
+ if (appInfo != null) {
+ return appInfo.areAttributionsUserVisible();
+ }
+ return false;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
/**
* Get the raw usages from the system, and then parse out the ones that are not recent enough,
* determine which permission group each belongs in, and removes duplicates (if the same app
@@ -346,7 +436,6 @@
* running/recent info, if the usage is a phone call, per permission group.
*
* @param opNames a list of op names to get usage for
- *
* @return A map of permission group -> list of usages that are recent or running
*/
private Map<String, List<OpUsage>> getOpUsages(List<String> opNames) {
@@ -378,6 +467,7 @@
List<String> attributionTags =
new ArrayList<>(opEntry.getAttributedOpEntries().keySet());
+
int numAttrEntries = opEntry.getAttributedOpEntries().size();
for (int attrOpEntryNum = 0; attrOpEntryNum < numAttrEntries; attrOpEntryNum++) {
String attributionTag = attributionTags.get(attrOpEntryNum);
@@ -456,6 +546,7 @@
ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>();
// map of usage.proxy hash -> usage hash, telling us if a usage is a proxy
ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>();
+
for (int i = 0; i < usages.size(); i++) {
OpUsage usage = usages.get(i);
allUsages.put(usage.getPackageIdHash(), usage);
@@ -588,7 +679,6 @@
} catch (PackageManager.NameNotFoundException e) {
// do nothing
}
-
}
usagesAndLabels.put(start.usage, proxyLabel);
}
diff --git a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
index 08a7205..f028ed3 100644
--- a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
+++ b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -24,6 +24,8 @@
import android.util.SparseArray;
import android.view.selectiontoolbar.ShowInfo;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.UUID;
/**
@@ -130,5 +132,22 @@
}
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ int size = mToolbarCache.size();
+ pw.print("number selectionToolbar: "); pw.println(size);
+ String pfx = " ";
+ for (int i = 0; i < size; i++) {
+ pw.print("#"); pw.println(i);
+ int callingUid = mToolbarCache.keyAt(i);
+ pw.print(pfx); pw.print("callingUid: "); pw.println(callingUid);
+ Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
+ RemoteSelectionToolbar selectionToolbar = toolbarPair.second;
+ pw.print(pfx); pw.print("selectionToolbar: ");
+ selectionToolbar.dump(pfx, pw);
+ pw.println();
+ }
+ }
}
diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
index 04491f0..8fe6f71 100644
--- a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
+++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
@@ -24,6 +24,8 @@
import android.view.MotionEvent;
import android.widget.LinearLayout;
+import java.io.PrintWriter;
+
/**
* This class is the root view for the selection toolbar. It is responsible for
* detecting the click on the item and to also transfer input focus to the application.
@@ -40,6 +42,9 @@
private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
private Rect mContentRect;
+ private int mLastDownX = -1;
+ private int mLastDownY = -1;
+
public FloatingToolbarRoot(Context context, IBinder targetInputToken,
SelectionToolbarRenderService.TransferTouchListener transferTouchListener) {
super(context);
@@ -59,13 +64,13 @@
@SuppressLint("ClickableViewAccessibility")
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- int downX = (int) event.getX();
- int downY = (int) event.getY();
+ mLastDownX = (int) event.getX();
+ mLastDownY = (int) event.getY();
if (DEBUG) {
- Log.d(TAG, "downX=" + downX + " downY=" + downY);
+ Log.d(TAG, "downX=" + mLastDownX + " downY=" + mLastDownY);
}
// TODO(b/215497659): Check FLAG_WINDOW_IS_PARTIALLY_OBSCURED
- if (!mContentRect.contains(downX, downY)) {
+ if (!mContentRect.contains(mLastDownX, mLastDownY)) {
if (DEBUG) {
Log.d(TAG, "Transfer touch focus to application.");
}
@@ -75,4 +80,10 @@
}
return super.dispatchTouchEvent(event);
}
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.println("FloatingToolbarRoot:");
+ pw.print(prefix + " "); pw.print("last down X: "); pw.println(mLastDownX);
+ pw.print(prefix + " "); pw.print("last down Y: "); pw.println(mLastDownY);
+ }
}
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
index 9a11923..d75fbc0 100644
--- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -58,6 +58,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
+import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@@ -1372,4 +1373,32 @@
Log.v(TAG, message);
}
}
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("toolbar token: "); pw.println(mSelectionToolbarToken);
+ pw.print(prefix); pw.print("dismissed: "); pw.println(mDismissed);
+ pw.print(prefix); pw.print("hidden: "); pw.println(mHidden);
+ pw.print(prefix); pw.print("popup width: "); pw.println(mPopupWidth);
+ pw.print(prefix); pw.print("popup height: "); pw.println(mPopupHeight);
+ pw.print(prefix); pw.print("relative coords: "); pw.println(mRelativeCoordsForToolbar);
+ pw.print(prefix); pw.print("main panel size: "); pw.println(mMainPanelSize);
+ boolean hasOverflow = hasOverflow();
+ pw.print(prefix); pw.print("has overflow: "); pw.println(hasOverflow);
+ if (hasOverflow) {
+ pw.print(prefix); pw.print("overflow open: "); pw.println(mIsOverflowOpen);
+ pw.print(prefix); pw.print("overflow size: "); pw.println(mOverflowPanelSize);
+ }
+ if (mSurfaceControlViewHost != null) {
+ FloatingToolbarRoot root = (FloatingToolbarRoot) mSurfaceControlViewHost.getView();
+ root.dump(prefix, pw);
+ }
+ if (mMenuItems != null) {
+ int menuItemSize = mMenuItems.size();
+ pw.print(prefix); pw.print("number menu items: "); pw.println(menuItemSize);
+ for (int i = 0; i < menuItemSize; i++) {
+ pw.print(prefix); pw.print("#"); pw.println(i);
+ pw.print(prefix + " "); pw.println(mMenuItems.get(i));
+ }
+ }
+ }
}
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 271e307..cd18c19 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -32,21 +32,6 @@
* Constants for supporting speech recognition through starting an {@link Intent}
*/
public class RecognizerIntent {
- /**
- * The extra key used in an intent to the speech recognizer for voice search. Not
- * generally to be used by developers. The system search dialog uses this, for example,
- * to set a calling package for identification by a voice search API. If this extra
- * is set by anyone but the system process, it should be overridden by the voice search
- * implementation.
- */
- public static final String EXTRA_CALLING_PACKAGE = "calling_package";
-
- /**
- * The extra key used in an intent which is providing an already opened audio source for the
- * RecognitionService to use. Data should be a URI to an audio resource.
- */
- public static final String EXTRA_AUDIO_INJECT_SOURCE =
- "android.speech.extra.AUDIO_INJECT_SOURCE";
private RecognizerIntent() {
// Not for instantiating.
@@ -58,7 +43,7 @@
* {@link Activity#onActivityResult}, if you start the intent using
* {@link Activity#startActivityForResult(Intent, int)}), or forwarded via a PendingIntent
* if one is provided.
- *
+ *
* <p>Starting this intent with just {@link Activity#startActivity(Intent)} is not supported.
* You must either use {@link Activity#startActivityForResult(Intent, int)}, or provide a
* PendingIntent, to receive recognition results.
@@ -70,7 +55,7 @@
* <ul>
* <li>{@link #EXTRA_LANGUAGE_MODEL}
* </ul>
- *
+ *
* <p>Optional extras:
* <ul>
* <li>{@link #EXTRA_PROMPT}
@@ -79,12 +64,12 @@
* <li>{@link #EXTRA_RESULTS_PENDINGINTENT}
* <li>{@link #EXTRA_RESULTS_PENDINGINTENT_BUNDLE}
* </ul>
- *
+ *
* <p> Result extras (returned in the result, not to be specified in the request):
* <ul>
* <li>{@link #EXTRA_RESULTS}
* </ul>
- *
+ *
* <p>NOTE: There may not be any applications installed to handle this action, so you should
* make sure to catch {@link ActivityNotFoundException}.
*/
@@ -97,12 +82,12 @@
*
* <p>If you want to avoid triggering any type of action besides web search, you can use
* the {@link #EXTRA_WEB_SEARCH_ONLY} extra.
- *
+ *
* <p>Required extras:
* <ul>
* <li>{@link #EXTRA_LANGUAGE_MODEL}
* </ul>
- *
+ *
* <p>Optional extras:
* <ul>
* <li>{@link #EXTRA_PROMPT}
@@ -112,13 +97,13 @@
* <li>{@link #EXTRA_WEB_SEARCH_ONLY}
* <li>{@link #EXTRA_ORIGIN}
* </ul>
- *
+ *
* <p> Result extras (returned in the result, not to be specified in the request):
* <ul>
* <li>{@link #EXTRA_RESULTS}
* <li>{@link #EXTRA_CONFIDENCE_SCORES} (optional)
* </ul>
- *
+ *
* <p>NOTE: There may not be any applications installed to handle this action, so you should
* make sure to catch {@link ActivityNotFoundException}.
*/
@@ -157,6 +142,129 @@
"android.speech.action.VOICE_SEARCH_HANDS_FREE";
/**
+ * Optional {@link android.os.ParcelFileDescriptor} pointing to an already opened audio
+ * source for the recognizer to use. The caller of the recognizer is responsible for closing
+ * the audio. If this extra is not set or the recognizer does not support this feature, the
+ * recognizer will open the mic for audio and close it when the recognition is finished.
+ *
+ * <p>Along with this extra, please send {@link #EXTRA_AUDIO_SOURCE_CHANNEL_COUNT},
+ * {@link #EXTRA_AUDIO_SOURCE_ENCODING}, and {@link #EXTRA_AUDIO_SOURCE_SAMPLING_RATE}
+ * extras, otherwise the default values of these extras will be used.
+ *
+ * <p>Additionally, {@link #EXTRA_ENABLE_BIASING_DEVICE_CONTEXT} may have no effect when this
+ * extra is set.
+ *
+ * <p>This can also be used as the string value for {@link #EXTRA_SEGMENTED_SESSION} to
+ * enable segmented session mode. The audio must be passed in using this extra. The
+ * recognition session will end when and only when the audio is closed.
+ *
+ * @see #EXTRA_SEGMENTED_SESSION
+ */
+ public static final String EXTRA_AUDIO_SOURCE = "android.speech.extra.AUDIO_SOURCE";
+
+ /**
+ * Optional integer, to be used with {@link #EXTRA_AUDIO_SOURCE}, to indicate the number of
+ * channels in the audio. The default value is 1.
+ */
+ public static final String EXTRA_AUDIO_SOURCE_CHANNEL_COUNT =
+ "android.speech.extra.AUDIO_SOURCE_CHANNEL_COUNT";
+
+ /**
+ * Optional integer (from {@link android.media.AudioFormat}), to be used with
+ * {@link #EXTRA_AUDIO_SOURCE}, to indicate the audio encoding. The default value is
+ * {@link android.media.AudioFormat#ENCODING_PCM_16BIT}.
+ */
+ public static final String EXTRA_AUDIO_SOURCE_ENCODING =
+ "android.speech.extra.AUDIO_SOURCE_ENCODING";
+
+ /**
+ * Optional integer, to be used with {@link #EXTRA_AUDIO_SOURCE}, to indicate the sampling
+ * rate of the audio. The default value is 16000.
+ */
+ public static final String EXTRA_AUDIO_SOURCE_SAMPLING_RATE =
+ "android.speech.extra.AUDIO_SOURCE_SAMPLING_RATE";
+
+ /**
+ * Optional boolean to enable biasing towards device context. The recognizer will use the
+ * device context to tune the recognition results.
+ *
+ * <p>Depending on the recognizer implementation, this value may have no effect.
+ */
+ public static final String EXTRA_ENABLE_BIASING_DEVICE_CONTEXT =
+ "android.speech.extra.ENABLE_BIASING_DEVICE_CONTEXT";
+
+ /**
+ * Optional list of strings, towards which the recognizer should bias the recognition results.
+ * These are separate from the device context.
+ */
+ public static final String EXTRA_BIASING_STRINGS = "android.speech.extra.BIASING_STRINGS";
+
+ /**
+ * Optional string to enable text formatting (e.g. unspoken punctuation (examples: question
+ * mark, comma, period, etc.), capitalization, etc.) and specify the optimization strategy.
+ * If set, the partial and final result texts will be formatted. Each result list will
+ * contain two hypotheses in the order of 1) formatted text 2) raw text.
+ *
+ * <p>Depending on the recognizer implementation, this value may have no effect.
+ *
+ * @see #FORMATTING_OPTIMIZE_QUALITY
+ * @see #FORMATTING_OPTIMIZE_LATENCY
+ */
+ public static final String EXTRA_ENABLE_FORMATTING = "android.speech.extra.ENABLE_FORMATTING";
+
+ /**
+ * Optimizes formatting quality. This will increase latency but provide the highest
+ * punctuation quality. This is a value to use for {@link #EXTRA_ENABLE_FORMATTING}.
+ *
+ * @see #EXTRA_ENABLE_FORMATTING
+ */
+ public static final String FORMATTING_OPTIMIZE_QUALITY = "quality";
+ /**
+ * Optimizes formatting latency. This will result in a slightly lower quality of punctuation
+ * but can improve the experience for real-time use cases. This is a value to use for
+ * {@link #EXTRA_ENABLE_FORMATTING}.
+ *
+ * @see #EXTRA_ENABLE_FORMATTING
+ */
+ public static final String FORMATTING_OPTIMIZE_LATENCY = "latency";
+
+ /**
+ * Optional boolean, to be used with {@link #EXTRA_ENABLE_FORMATTING}, to prevent the
+ * recognizer adding punctuation after the last word of the partial results. The default is
+ * false.
+ */
+ public static final String EXTRA_HIDE_PARTIAL_TRAILING_PUNCTUATION =
+ "android.speech.extra.HIDE_PARTIAL_TRAILING_PUNCTUATION";
+
+ /**
+ * Optional boolean indicating whether the recognizer should mask the offensive words in
+ * recognition results. The Default is true.
+ */
+ public static final String EXTRA_MASK_OFFENSIVE_WORDS =
+ "android.speech.extra.MASK_OFFENSIVE_WORDS";
+
+ /**
+ * The extra key used in an intent to the speech recognizer for voice search. Not
+ * generally to be used by developers. The system search dialog uses this, for example,
+ * to set a calling package for identification by a voice search API. If this extra
+ * is set by anyone but the system process, it should be overridden by the voice search
+ * implementation.
+ */
+ public static final String EXTRA_CALLING_PACKAGE = "calling_package";
+
+ /**
+ * The extra key used in an intent which is providing an already opened audio source for the
+ * RecognitionService to use. Data should be a URI to an audio resource.
+ *
+ * <p>Depending on the recognizer implementation, this value may have no effect.
+ *
+ * @deprecated Replaced with {@link #EXTRA_AUDIO_SOURCE}
+ */
+ @Deprecated
+ public static final String EXTRA_AUDIO_INJECT_SOURCE =
+ "android.speech.extra.AUDIO_INJECT_SOURCE";
+
+ /**
* Optional boolean to indicate that a "hands free" voice search was performed while the device
* was in a secure mode. An example of secure mode is when the device's screen lock is active,
* and it requires some form of authentication to be unlocked.
@@ -168,24 +276,29 @@
public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
/**
- * The minimum length of an utterance. We will not stop recording before this amount of time.
- *
- * Note that it is extremely rare you'd want to specify this value in an intent. If you don't
- * have a very good reason to change these, you should leave them as they are. Note also that
- * certain values may cause undesired or unexpected results - use judiciously! Additionally,
- * depending on the recognizer implementation, these values may have no effect.
+ * Optional integer to indicate the minimum length of the recognition session. The recognizer
+ * will not stop recognizing speech before this amount of time.
+ *
+ * <p>Note that it is extremely rare you'd want to specify this value in an intent.
+ * Generally, it should be specified only when it is also used as the value for
+ * {@link #EXTRA_SEGMENTED_SESSION} to enable segmented session mode. Note also that certain
+ * values may cause undesired or unexpected results - use judiciously!
+ *
+ * <p>Depending on the recognizer implementation, these values may have no effect.
*/
public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS =
"android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
/**
- * The amount of time that it should take after we stop hearing speech to consider the input
- * complete.
- *
- * Note that it is extremely rare you'd want to specify this value in an intent. If
- * you don't have a very good reason to change these, you should leave them as they are. Note
- * also that certain values may cause undesired or unexpected results - use judiciously!
- * Additionally, depending on the recognizer implementation, these values may have no effect.
+ * The amount of time that it should take after the recognizer stops hearing speech to
+ * consider the input complete hence end the recognition session.
+ *
+ * <p>Note that it is extremely rare you'd want to specify this value in an intent.
+ * Generally, it should be specified only when it is also used as the value for
+ * {@link #EXTRA_SEGMENTED_SESSION} to enable segmented session mode. Note also that certain
+ * values may cause undesired or unexpected results - use judiciously!
+ *
+ * <p>Depending on the recognizer implementation, these values may have no effect.
*/
public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS =
"android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
@@ -193,8 +306,8 @@
/**
* The amount of time that it should take after we stop hearing speech to consider the input
* possibly complete. This is used to prevent the endpointer cutting off during very short
- * mid-speech pauses.
- *
+ * mid-speech pauses.
+ *
* Note that it is extremely rare you'd want to specify this value in an intent. If
* you don't have a very good reason to change these, you should leave them as they are. Note
* also that certain values may cause undesired or unexpected results - use judiciously!
@@ -208,21 +321,21 @@
* {@link #ACTION_RECOGNIZE_SPEECH}. The recognizer uses this
* information to fine tune the results. This extra is required. Activities implementing
* {@link #ACTION_RECOGNIZE_SPEECH} may interpret the values as they see fit.
- *
+ *
* @see #LANGUAGE_MODEL_FREE_FORM
* @see #LANGUAGE_MODEL_WEB_SEARCH
*/
public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL";
- /**
- * Use a language model based on free-form speech recognition. This is a value to use for
- * {@link #EXTRA_LANGUAGE_MODEL}.
+ /**
+ * Use a language model based on free-form speech recognition. This is a value to use for
+ * {@link #EXTRA_LANGUAGE_MODEL}.
* @see #EXTRA_LANGUAGE_MODEL
*/
public static final String LANGUAGE_MODEL_FREE_FORM = "free_form";
- /**
- * Use a language model based on web search terms. This is a value to use for
- * {@link #EXTRA_LANGUAGE_MODEL}.
+ /**
+ * Use a language model based on web search terms. This is a value to use for
+ * {@link #EXTRA_LANGUAGE_MODEL}.
* @see #EXTRA_LANGUAGE_MODEL
*/
public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search";
@@ -236,7 +349,7 @@
* {@link java.util.Locale#getDefault()}.
*/
public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
-
+
/**
* Optional value which can be used to indicate the referer url of a page in which
* speech was requested. For example, a web browser may choose to provide this for
@@ -244,12 +357,12 @@
*/
public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
- /**
+ /**
* Optional limit on the maximum number of results to return. If omitted the recognizer
* will choose how many results to return. Must be an integer.
*/
public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
-
+
/**
* Optional boolean, to be used with {@link #ACTION_WEB_SEARCH}, to indicate whether to
* only fire web searches in response to a user's speech. The default is false, meaning
@@ -267,18 +380,18 @@
/**
* When the intent is {@link #ACTION_RECOGNIZE_SPEECH}, the speech input activity will
* return results to you via the activity results mechanism. Alternatively, if you use this
- * extra to supply a PendingIntent, the results will be added to its bundle and the
+ * extra to supply a PendingIntent, the results will be added to its bundle and the
* PendingIntent will be sent to its target.
*/
- public static final String EXTRA_RESULTS_PENDINGINTENT =
+ public static final String EXTRA_RESULTS_PENDINGINTENT =
"android.speech.extra.RESULTS_PENDINGINTENT";
-
+
/**
* If you use {@link #EXTRA_RESULTS_PENDINGINTENT} to supply a forwarding intent, you can
* also use this extra to supply additional extras for the final intent. The search results
* will be added to this bundle, and the combined bundle will be sent to the target.
*/
- public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE =
+ public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE =
"android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
/** Result code returned when no matches are found for the given speech */
@@ -301,7 +414,7 @@
* the lack of this extra indicates failure.
*/
public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS";
-
+
/**
* A float array of confidence scores of the recognition results when performing
* {@link #ACTION_RECOGNIZE_SPEECH}. The array should be the same size as the ArrayList
@@ -317,7 +430,7 @@
* returned in an activity result.
*/
public static final String EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES";
-
+
/**
* Returns the broadcast intent to fire with
* {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}
@@ -334,7 +447,7 @@
* (Whether these are actually provided is up to the particular implementation. It is
* recommended that {@link Activity}s implementing {@link #ACTION_WEB_SEARCH} provide this
* information, but it is not required.)
- *
+ *
* @param context a context object
* @return the broadcast intent to fire or null if not available
*/
@@ -343,15 +456,15 @@
ResolveInfo ri = context.getPackageManager().resolveActivity(
voiceSearchIntent, PackageManager.GET_META_DATA);
if (ri == null || ri.activityInfo == null || ri.activityInfo.metaData == null) return null;
-
+
String className = ri.activityInfo.metaData.getString(DETAILS_META_DATA);
if (className == null) return null;
-
+
Intent detailsIntent = new Intent(ACTION_GET_LANGUAGE_DETAILS);
detailsIntent.setComponent(new ComponentName(ri.activityInfo.packageName, className));
return detailsIntent;
}
-
+
/**
* Meta-data name under which an {@link Activity} implementing {@link #ACTION_WEB_SEARCH} can
* use to expose the class name of a {@link BroadcastReceiver} which can respond to request for
@@ -370,7 +483,7 @@
* are required to implement this. Thus retrieving this meta-data may be null.
*/
public static final String DETAILS_META_DATA = "android.speech.DETAILS";
-
+
/**
* A broadcast intent which can be fired to the {@link BroadcastReceiver} component specified
* in the meta-data defined in the {@link #DETAILS_META_DATA} meta-data of an
@@ -388,7 +501,7 @@
*/
public static final String ACTION_GET_LANGUAGE_DETAILS =
"android.speech.action.GET_LANGUAGE_DETAILS";
-
+
/**
* Specify this boolean extra in a broadcast of {@link #ACTION_GET_LANGUAGE_DETAILS} to
* indicate that only the current language preference is needed in the response. This
@@ -397,7 +510,7 @@
*/
public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE =
"android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE";
-
+
/**
* The key to the extra in the {@link Bundle} returned by {@link #ACTION_GET_LANGUAGE_DETAILS}
* which is a {@link String} that represents the current language preference this user has
@@ -405,7 +518,7 @@
*/
public static final String EXTRA_LANGUAGE_PREFERENCE =
"android.speech.extra.LANGUAGE_PREFERENCE";
-
+
/**
* The key to the extra in the {@link Bundle} returned by {@link #ACTION_GET_LANGUAGE_DETAILS}
* which is an {@link ArrayList} of {@link String}s that represents the languages supported by
@@ -428,14 +541,21 @@
public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
/**
- * Optional boolean, when true and supported by the recognizer implementation it will split
- * the recognition results in segments, returned via
- * {@link RecognitionListener#onSegmentResults(Bundle)} and terminate the session with
- * {@link RecognitionListener#onEndOfSegmentedSession()}. There will be no call to
- * {@link RecognitionListener#onResults(Bundle)}. Callers can use
- * {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} and
- * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS} to tune how long the segments
- * will be. Defaults to false.
+ * Optional string to enable segmented session mode of the specified type, which can be
+ * {@link #EXTRA_AUDIO_SOURCE}, {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} or
+ * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS}. When segmented session mode is
+ * supported by the recognizer implementation and this extra is set, it will return the
+ * recognition results in segments via {@link RecognitionListener#onSegmentResults(Bundle)}
+ * and terminate the session with {@link RecognitionListener#onEndOfSegmentedSession()}.
+ *
+ * <p>When setting this extra, make sure the extra used as the string value here is also set
+ * in the same intent with proper value.
+ *
+ * <p>Depending on the recognizer implementation, this value may have no effect.
+ *
+ * @see #EXTRA_AUDIO_SOURCE
+ * @see #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS
+ * @see #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS
*/
- public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
+ public static final String EXTRA_SEGMENTED_SESSION = "android.speech.extra.SEGMENTED_SESSION";
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index fce95c8..5500fb8 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -89,7 +89,9 @@
ITYPE_BOTTOM_DISPLAY_CUTOUT,
ITYPE_IME,
ITYPE_CLIMATE_BAR,
- ITYPE_EXTRA_NAVIGATION_BAR
+ ITYPE_EXTRA_NAVIGATION_BAR,
+ ITYPE_LOCAL_NAVIGATION_BAR_1,
+ ITYPE_LOCAL_NAVIGATION_BAR_2
})
public @interface InternalInsetsType {}
@@ -132,7 +134,11 @@
public static final int ITYPE_CLIMATE_BAR = 20;
public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;
- static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
+ /** Additional types for local insets. **/
+ public static final int ITYPE_LOCAL_NAVIGATION_BAR_1 = 22;
+ public static final int ITYPE_LOCAL_NAVIGATION_BAR_2 = 23;
+
+ static final int LAST_TYPE = ITYPE_LOCAL_NAVIGATION_BAR_2;
public static final int SIZE = LAST_TYPE + 1;
// Derived types
@@ -674,6 +680,8 @@
if ((types & Type.NAVIGATION_BARS) != 0) {
result.add(ITYPE_NAVIGATION_BAR);
result.add(ITYPE_EXTRA_NAVIGATION_BAR);
+ result.add(ITYPE_LOCAL_NAVIGATION_BAR_1);
+ result.add(ITYPE_LOCAL_NAVIGATION_BAR_2);
}
if ((types & Type.CAPTION_BAR) != 0) {
result.add(ITYPE_CAPTION_BAR);
@@ -714,6 +722,8 @@
return Type.STATUS_BARS;
case ITYPE_NAVIGATION_BAR:
case ITYPE_EXTRA_NAVIGATION_BAR:
+ case ITYPE_LOCAL_NAVIGATION_BAR_1:
+ case ITYPE_LOCAL_NAVIGATION_BAR_2:
return Type.NAVIGATION_BARS;
case ITYPE_CAPTION_BAR:
return Type.CAPTION_BAR;
@@ -833,6 +843,10 @@
return "ITYPE_CLIMATE_BAR";
case ITYPE_EXTRA_NAVIGATION_BAR:
return "ITYPE_EXTRA_NAVIGATION_BAR";
+ case ITYPE_LOCAL_NAVIGATION_BAR_1:
+ return "ITYPE_LOCAL_NAVIGATION_BAR_1";
+ case ITYPE_LOCAL_NAVIGATION_BAR_2:
+ return "ITYPE_LOCAL_NAVIGATION_BAR_2";
default:
return "ITYPE_UNKNOWN_" + type;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68c8143..3b2a248 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1624,7 +1624,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_postNotification"
android:description="@string/permdesc_postNotification"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous|instant" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- ====================================================================== -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 21b2cb0..2d2c03b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -544,6 +544,8 @@
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
<permission name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES" />
+ <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+ <permission name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index e50ad38..9b41468 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -52,9 +52,6 @@
*/
public class BackAnimationController implements RemoteCallable<BackAnimationController> {
- private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
- public static final boolean IS_ENABLED = SystemProperties
- .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
"persist.debug.back_predictability_progress_threshold";
private static final int PROGRESS_THRESHOLD = SystemProperties
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 79a24b7..ee4d5ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -283,9 +283,8 @@
if (context == null) {
return;
}
- LetterboxEduWindowManager newLayout = new LetterboxEduWindowManager(context, taskInfo,
- mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
- this::onLetterboxEduDismissed);
+ LetterboxEduWindowManager newLayout = createLetterboxEduWindowManager(context, taskInfo,
+ taskListener);
if (newLayout.createLayout(showOnDisplay(taskInfo.displayId))) {
// The new layout is eligible to be shown, make it the active layout.
if (mActiveLetterboxEduLayout != null) {
@@ -298,6 +297,14 @@
}
}
+ @VisibleForTesting
+ LetterboxEduWindowManager createLetterboxEduWindowManager(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ return new LetterboxEduWindowManager(context, taskInfo,
+ mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
+ this::onLetterboxEduDismissed);
+ }
+
private void onLetterboxEduDismissed() {
mActiveLetterboxEduLayout = null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index 5679bc4..face243 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -22,6 +22,10 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
+
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
@@ -110,7 +114,8 @@
* @param canShow whether the layout is allowed to be shown by the parent controller.
* @return whether the layout is eligible to be shown.
*/
- protected boolean createLayout(boolean canShow) {
+ @VisibleForTesting(visibility = PROTECTED)
+ public boolean createLayout(boolean canShow) {
if (!eligibleToShowLayout()) {
return false;
}
@@ -184,7 +189,8 @@
* @param canShow whether the layout is allowed to be shown by the parent controller.
* @return whether the layout is eligible to be shown.
*/
- protected boolean updateCompatInfo(TaskInfo taskInfo,
+ @VisibleForTesting(visibility = PROTECTED)
+ public boolean updateCompatInfo(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
final Configuration prevTaskConfig = mTaskConfig;
final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
@@ -201,7 +207,8 @@
View layout = getLayout();
if (layout == null || prevTaskListener != taskListener) {
- // TaskListener changed, recreate the layout for new surface parent.
+ // Layout wasn't created yet or TaskListener changed, recreate the layout for new
+ // surface parent.
release();
return createLayout(canShow);
}
@@ -227,7 +234,8 @@
*
* @param canShow whether the layout is allowed to be shown by the parent controller.
*/
- void updateVisibility(boolean canShow) {
+ @VisibleForTesting(visibility = PACKAGE)
+ public void updateVisibility(boolean canShow) {
View layout = getLayout();
if (layout == null) {
// Layout may not have been created because it was hidden previously.
@@ -242,7 +250,8 @@
}
/** Called when display layout changed. */
- void updateDisplayLayout(DisplayLayout displayLayout) {
+ @VisibleForTesting(visibility = PACKAGE)
+ public void updateDisplayLayout(DisplayLayout displayLayout) {
final Rect prevStableBounds = mStableBounds;
final Rect curStableBounds = new Rect();
displayLayout.getStableBounds(curStableBounds);
@@ -255,7 +264,7 @@
}
/** Called when the surface is ready to be placed under the task surface. */
- @VisibleForTesting
+ @VisibleForTesting(visibility = PRIVATE)
void attachToParentSurface(SurfaceControl.Builder b) {
mTaskListener.attachChildSurfaceToTask(mTaskId, b);
}
@@ -347,8 +356,9 @@
return result;
}
- @VisibleForTesting
- SurfaceControlViewHost createSurfaceViewHost() {
+ /** Creates a {@link SurfaceControlViewHost} for this window manager. */
+ @VisibleForTesting(visibility = PRIVATE)
+ public SurfaceControlViewHost createSurfaceViewHost() {
return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
index 3810eca..03986ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
@@ -39,7 +39,6 @@
/**
* Controls the enter/exit animations of the letterbox education.
*/
-// TODO(b/215316431): Add tests
class LetterboxEduAnimationController {
private static final String TAG = "LetterboxEduAnimation";
@@ -99,15 +98,10 @@
/**
* Starts both the background dim fade-out animation and the dialog exit animation.
*/
- void startExitAnimation(@Nullable LetterboxEduDialogLayout layout, Runnable endCallback) {
+ void startExitAnimation(@NonNull LetterboxEduDialogLayout layout, Runnable endCallback) {
// Cancel any previous animation if it's still running.
cancelAnimation();
- if (layout == null) {
- endCallback.run();
- return;
- }
-
final View dialogContainer = layout.getDialogContainer();
mDialogAnimation = loadAnimation(WindowAnimation_windowExitAnimation);
if (mDialogAnimation == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
index fc6fd3f..02197f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
@@ -29,18 +29,28 @@
/**
* Custom layout for Letterbox Education dialog action.
*/
-// TODO(b/215316431): Add tests
class LetterboxEduDialogActionLayout extends FrameLayout {
- LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
+ public LetterboxEduDialogActionLayout(Context context) {
+ this(context, null);
+ }
+
+ public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
TypedArray styledAttributes =
context.getTheme().obtainStyledAttributes(
- attrs,
- R.styleable.LetterboxEduDialogActionLayout,
- /* defStyleAttr= */ 0,
- /* defStyleRes= */ 0);
+ attrs, R.styleable.LetterboxEduDialogActionLayout, defStyleAttr,
+ defStyleRes);
int iconId = styledAttributes.getResourceId(
R.styleable.LetterboxEduDialogActionLayout_icon, 0);
String text = styledAttributes.getString(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
index bb6fe98..2da6a6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.compatui.letterboxedu;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@@ -31,7 +32,6 @@
* <p>This layout should fill the entire task and the background around the dialog acts as the
* background dim which dismisses the dialog when clicked.
*/
-// TODO(b/215316431): Add tests
class LetterboxEduDialogLayout extends ConstraintLayout {
// The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
@@ -68,16 +68,16 @@
/**
* Register a callback for the dismiss button and background dim.
*
- * @param callback The callback to register
+ * @param callback The callback to register or null if all on click listeners should be removed.
*/
- void setDismissOnClickListener(Runnable callback) {
- findViewById(R.id.letterbox_education_dialog_dismiss_button).setOnClickListener(
- view -> callback.run());
+ void setDismissOnClickListener(@Nullable Runnable callback) {
+ final OnClickListener listener = callback == null ? null : view -> callback.run();
+ findViewById(R.id.letterbox_education_dialog_dismiss_button).setOnClickListener(listener);
// Clicks on the background dim should also dismiss the dialog.
- setOnClickListener(view -> callback.run());
+ setOnClickListener(listener);
// We add a no-op on-click listener to the dialog container so that clicks on it won't
// propagate to the listener of the layout (which represents the background dim).
- mDialogContainer.setOnClickListener(view -> {});
+ mDialogContainer.setOnClickListener(callback == null ? null : view -> {});
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
index bb4d427..30b9f08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
@@ -29,6 +29,7 @@
import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayLayout;
@@ -38,7 +39,6 @@
/**
* Window manager for the Letterbox Education.
*/
-// TODO(b/215316431): Add tests
public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
/**
@@ -51,7 +51,8 @@
* The name of the {@link SharedPreferences} that holds which user has seen the Letterbox
* Education for specific packages and which user has seen the full dialog for any package.
*/
- private static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
+ @VisibleForTesting
+ static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
"has_seen_letterbox_education";
/**
@@ -65,19 +66,37 @@
private boolean mEligibleForLetterboxEducation;
@Nullable
- private LetterboxEduDialogLayout mLayout;
+ @VisibleForTesting
+ LetterboxEduDialogLayout mLayout;
private final Runnable mOnDismissCallback;
+ /**
+ * The vertical margin between the dialog container and the task stable bounds (excluding
+ * insets).
+ */
+ private final int mDialogVerticalMargin;
+
public LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
DisplayLayout displayLayout, Runnable onDismissCallback) {
+ this(context, taskInfo, syncQueue, taskListener, displayLayout, onDismissCallback,
+ new LetterboxEduAnimationController(context));
+ }
+
+ @VisibleForTesting
+ LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
+ SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+ DisplayLayout displayLayout, Runnable onDismissCallback,
+ LetterboxEduAnimationController animationController) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mOnDismissCallback = onDismissCallback;
+ mAnimationController = animationController;
mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
- mAnimationController = new LetterboxEduAnimationController(context);
mSharedPreferences = mContext.getSharedPreferences(HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
Context.MODE_PRIVATE);
+ mDialogVerticalMargin = (int) mContext.getResources().getDimension(
+ R.dimen.letterbox_education_dialog_margin);
}
@Override
@@ -124,13 +143,12 @@
}
final View dialogContainer = mLayout.getDialogContainer();
MarginLayoutParams marginParams = (MarginLayoutParams) dialogContainer.getLayoutParams();
- int verticalMargin = (int) mContext.getResources().getDimension(
- R.dimen.letterbox_education_dialog_margin);
final Rect taskBounds = getTaskBounds();
final Rect taskStableBounds = getTaskStableBounds();
- marginParams.topMargin = taskStableBounds.top - taskBounds.top + verticalMargin;
- marginParams.bottomMargin = taskBounds.bottom - taskStableBounds.bottom + verticalMargin;
+ marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
+ marginParams.bottomMargin =
+ taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
dialogContainer.setLayoutParams(marginParams);
}
@@ -147,6 +165,10 @@
}
private void onDismiss() {
+ if (mLayout == null) {
+ return;
+ }
+ mLayout.setDismissOnClickListener(null);
mAnimationController.startExitAnimation(mLayout, () -> {
release();
mOnDismissCallback.run();
@@ -204,7 +226,8 @@
return String.valueOf(mContext.getUserId());
}
- private boolean isTaskbarEduShowing() {
+ @VisibleForTesting
+ boolean isTaskbarEduShowing() {
return Settings.Secure.getInt(mContext.getContentResolver(),
LAUNCHER_TASKBAR_EDUCATION_SHOWING, /* def= */ 0) == 1;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 0362b3f..c94f3d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -699,10 +699,7 @@
Context context,
@ShellMainThread ShellExecutor shellExecutor
) {
- if (BackAnimationController.IS_ENABLED) {
- return Optional.of(
- new BackAnimationController(shellExecutor, context));
- }
- return Optional.empty();
+ return Optional.of(
+ new BackAnimationController(shellExecutor, context));
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index c5be485..29e40be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -52,6 +52,7 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
import org.junit.Before;
import org.junit.Test;
@@ -81,7 +82,8 @@
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
private @Mock SyncTransactionQueue mMockSyncQueue;
private @Mock ShellExecutor mMockExecutor;
- private @Mock CompatUIWindowManager mMockLayout;
+ private @Mock CompatUIWindowManager mMockCompatLayout;
+ private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -91,16 +93,26 @@
MockitoAnnotations.initMocks(this);
doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
- doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
- doReturn(TASK_ID).when(mMockLayout).getTaskId();
- doReturn(true).when(mMockLayout).createLayout(anyBoolean());
- doReturn(true).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
+ doReturn(DISPLAY_ID).when(mMockCompatLayout).getDisplayId();
+ doReturn(TASK_ID).when(mMockCompatLayout).getTaskId();
+ doReturn(true).when(mMockCompatLayout).createLayout(anyBoolean());
+ doReturn(true).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
+ doReturn(DISPLAY_ID).when(mMockLetterboxEduLayout).getDisplayId();
+ doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
+ doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
+ doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
mController = new CompatUIController(mContext, mMockDisplayController,
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
- return mMockLayout;
+ return mMockCompatLayout;
+ }
+
+ @Override
+ LetterboxEduWindowManager createLetterboxEduWindowManager(Context context,
+ TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ return mMockLetterboxEduLayout;
}
};
spyOn(mController);
@@ -121,68 +133,95 @@
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+ verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
- // Verify that the compat controls are updated with new size compat info.
- clearInvocations(mMockLayout);
+ // Verify that the compat controls and letterbox education are updated with new size compat
+ // info.
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
clearInvocations(mController);
taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ true);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ true);
- // Verify that compat controls are removed with null task listener.
- clearInvocations(mMockLayout);
+ // Verify that compat controls and letterbox education are removed with null task listener.
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
clearInvocations(mController);
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN),
/* taskListener= */ null);
- verify(mMockLayout).release();
+ verify(mMockCompatLayout).release();
+ verify(mMockLetterboxEduLayout).release();
}
@Test
public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
- doReturn(false).when(mMockLayout).createLayout(anyBoolean());
+ doReturn(false).when(mMockCompatLayout).createLayout(anyBoolean());
+ doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+ verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
// Verify that the layout is created again.
- clearInvocations(mMockLayout);
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
clearInvocations(mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+ verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+ verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+ verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
}
@Test
public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
- doReturn(false).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
+ doReturn(false).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
+ doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+ verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
- clearInvocations(mMockLayout);
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
clearInvocations(mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ true);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ true);
// Verify that the layout is created again.
- clearInvocations(mMockLayout);
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
clearInvocations(mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+ verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+ verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+ verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
}
@@ -204,14 +243,16 @@
mController.onDisplayRemoved(DISPLAY_ID + 1);
- verify(mMockLayout, never()).release();
+ verify(mMockCompatLayout, never()).release();
+ verify(mMockLetterboxEduLayout, never()).release();
verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
any());
mController.onDisplayRemoved(DISPLAY_ID);
verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
- verify(mMockLayout).release();
+ verify(mMockCompatLayout).release();
+ verify(mMockLetterboxEduLayout).release();
}
@Test
@@ -221,11 +262,13 @@
mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
- verify(mMockLayout, never()).updateDisplayLayout(any());
+ verify(mMockCompatLayout, never()).updateDisplayLayout(any());
+ verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(any());
mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
- verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
}
@Test
@@ -242,104 +285,125 @@
mOnInsetsChangedListenerCaptor.capture());
mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);
- verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
// No update if the insets state is the same.
- clearInvocations(mMockLayout);
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
- verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+ verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
}
@Test
- public void testChangeButtonVisibilityOnImeShowHide() {
+ public void testChangeLayoutsVisibilityOnImeShowHide() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after IME is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
- verify(mMockLayout).updateVisibility(false);
+ verify(mMockCompatLayout).updateVisibility(false);
+ verify(mMockLetterboxEduLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ false);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ false);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
- verify(mMockLayout).updateVisibility(true);
+ verify(mMockCompatLayout).updateVisibility(true);
+ verify(mMockLetterboxEduLayout).updateVisibility(true);
}
@Test
- public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
+ public void testChangeLayoutsVisibilityOnKeyguardOccludedChanged() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after keyguard becomes occluded.
mController.onKeyguardOccludedChanged(true);
- verify(mMockLayout).updateVisibility(false);
+ verify(mMockCompatLayout).updateVisibility(false);
+ verify(mMockLetterboxEduLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
+ verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ false);
+ verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+ false);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
- verify(mMockLayout).updateVisibility(true);
+ verify(mMockCompatLayout).updateVisibility(true);
+ verify(mMockLetterboxEduLayout).updateVisibility(true);
}
@Test
- public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
+ public void testLayoutsRemainHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
mController.onKeyguardOccludedChanged(true);
- verify(mMockLayout, times(2)).updateVisibility(false);
+ verify(mMockCompatLayout, times(2)).updateVisibility(false);
+ verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
- clearInvocations(mMockLayout);
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
// Verify button remains hidden after keyguard becomes not occluded since IME is showing.
mController.onKeyguardOccludedChanged(false);
- verify(mMockLayout).updateVisibility(false);
+ verify(mMockCompatLayout).updateVisibility(false);
+ verify(mMockLetterboxEduLayout).updateVisibility(false);
// Verify button is shown after IME is not showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
- verify(mMockLayout).updateVisibility(true);
+ verify(mMockCompatLayout).updateVisibility(true);
+ verify(mMockLetterboxEduLayout).updateVisibility(true);
}
@Test
- public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
+ public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsOccluded() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
mController.onKeyguardOccludedChanged(true);
- verify(mMockLayout, times(2)).updateVisibility(false);
+ verify(mMockCompatLayout, times(2)).updateVisibility(false);
+ verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
- clearInvocations(mMockLayout);
+ clearInvocations(mMockCompatLayout);
+ clearInvocations(mMockLetterboxEduLayout);
// Verify button remains hidden after IME is hidden since keyguard is occluded.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
- verify(mMockLayout).updateVisibility(false);
+ verify(mMockCompatLayout).updateVisibility(false);
+ verify(mMockLetterboxEduLayout).updateVisibility(false);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
- verify(mMockLayout).updateVisibility(true);
+ verify(mMockCompatLayout).updateVisibility(true);
+ verify(mMockLetterboxEduLayout).updateVisibility(true);
}
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
new file mode 100644
index 0000000..00e4938
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterboxedu;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link LetterboxEduDialogLayout}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:LetterboxEduDialogLayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LetterboxEduDialogLayoutTest extends ShellTestCase {
+
+ @Mock
+ private Runnable mDismissCallback;
+
+ private LetterboxEduDialogLayout mLayout;
+ private View mDismissButton;
+ private View mDialogContainer;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mLayout = (LetterboxEduDialogLayout)
+ LayoutInflater.from(mContext).inflate(R.layout.letterbox_education_dialog_layout,
+ null);
+ mDismissButton = mLayout.findViewById(R.id.letterbox_education_dialog_dismiss_button);
+ mDialogContainer = mLayout.findViewById(R.id.letterbox_education_dialog_container);
+ mLayout.setDismissOnClickListener(mDismissCallback);
+ }
+
+ @Test
+ public void testOnFinishInflate() {
+ assertEquals(mLayout.getDialogContainer(),
+ mLayout.findViewById(R.id.letterbox_education_dialog_container));
+ assertEquals(mLayout.getBackgroundDim(), mLayout.getBackground());
+ assertEquals(mLayout.getBackground().getAlpha(), 0);
+ }
+
+ @Test
+ public void testOnDismissButtonClicked() {
+ assertTrue(mDismissButton.performClick());
+
+ verify(mDismissCallback).run();
+ }
+
+ @Test
+ public void testOnBackgroundClicked() {
+ assertTrue(mLayout.performClick());
+
+ verify(mDismissCallback).run();
+ }
+
+ @Test
+ public void testOnDialogContainerClicked() {
+ assertTrue(mDialogContainer.performClick());
+
+ verify(mDismissCallback, never()).run();
+ }
+
+ @Test
+ public void testSetDismissOnClickListenerNull() {
+ mLayout.setDismissOnClickListener(null);
+
+ assertFalse(mDismissButton.performClick());
+ assertFalse(mLayout.performClick());
+ assertFalse(mDialogContainer.performClick());
+
+ verify(mDismissCallback, never()).run();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
new file mode 100644
index 0000000..0509dd3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterboxedu;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.SurfaceControlViewHost;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link LetterboxEduWindowManager}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:LetterboxEduWindowManagerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LetterboxEduWindowManagerTest extends ShellTestCase {
+
+ private static final int TASK_ID = 1;
+
+ private static final int TASK_WIDTH = 200;
+ private static final int TASK_HEIGHT = 100;
+ private static final int DISPLAY_CUTOUT_TOP = 5;
+ private static final int DISPLAY_CUTOUT_BOTTOM = 10;
+ private static final int DISPLAY_CUTOUT_HORIZONTAL = 20;
+
+ @Captor
+ private ArgumentCaptor<WindowManager.LayoutParams> mWindowAttrsCaptor;
+ @Captor
+ private ArgumentCaptor<Runnable> mEndCallbackCaptor;
+
+ @Mock private LetterboxEduAnimationController mAnimationController;
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private SurfaceControlViewHost mViewHost;
+ @Mock private Runnable mOnDismissCallback;
+
+ private SharedPreferences mSharedPreferences;
+ private String mPrefKey;
+ @Nullable
+ private Boolean mInitialPrefValue = null;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mSharedPreferences = mContext.getSharedPreferences(
+ LetterboxEduWindowManager.HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
+ Context.MODE_PRIVATE);
+ mPrefKey = String.valueOf(mContext.getUserId());
+ if (mSharedPreferences.contains(mPrefKey)) {
+ mInitialPrefValue = mSharedPreferences.getBoolean(mPrefKey, /* default= */ false);
+ mSharedPreferences.edit().remove(mPrefKey).apply();
+ }
+ }
+
+ @After
+ public void tearDown() {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ if (mInitialPrefValue == null) {
+ editor.remove(mPrefKey);
+ } else {
+ editor.putBoolean(mPrefKey, mInitialPrefValue);
+ }
+ editor.apply();
+ }
+
+ @Test
+ public void testCreateLayout_notEligible_doesNotCreateLayout() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
+
+ assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
+ public void testCreateLayout_alreadyShownToUser_doesNotCreateLayout() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+ mSharedPreferences.edit().putBoolean(mPrefKey, true).apply();
+
+ assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
+ public void testCreateLayout_taskBarEducationIsShowing_doesNotCreateLayout() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */
+ true, /* isTaskbarEduShowing= */ true);
+
+ assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
+ public void testCreateLayout_canShowFalse_returnsTrueButDoesNotCreateLayout() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ false));
+
+ assertFalse(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
+ public void testCreateLayout_canShowTrue_createsLayoutCorrectly() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+
+ assertTrue(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+ LetterboxEduDialogLayout layout = windowManager.mLayout;
+ assertNotNull(layout);
+ verify(mViewHost).setView(eq(layout), mWindowAttrsCaptor.capture());
+ verifyLayout(layout, mWindowAttrsCaptor.getValue(), /* expectedWidth= */ TASK_WIDTH,
+ /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */ DISPLAY_CUTOUT_TOP,
+ /* expectedExtraBottomMargin= */ DISPLAY_CUTOUT_BOTTOM);
+
+ // Clicking the layout does nothing until enter animation is done.
+ layout.performClick();
+ verify(mAnimationController, never()).startExitAnimation(any(), any());
+
+ verifyAndFinishEnterAnimation(layout);
+
+ // Exit animation should start following a click on the layout.
+ layout.performClick();
+
+ // Window manager isn't released until exit animation is done.
+ verify(windowManager, never()).release();
+
+ // Verify multiple clicks are ignored.
+ layout.performClick();
+
+ verifyAndFinishExitAnimation(layout);
+
+ verify(windowManager).release();
+ verify(mOnDismissCallback).run();
+ }
+
+ @Test
+ public void testUpdateCompatInfo_updatesLayoutCorrectly() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+ LetterboxEduDialogLayout layout = windowManager.mLayout;
+ assertNotNull(layout);
+
+ assertTrue(windowManager.updateCompatInfo(
+ createTaskInfo(/* eligible= */ true, new Rect(50, 25, 150, 75)),
+ mTaskListener, /* canShow= */ true));
+
+ verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ 100,
+ /* expectedHeight= */ 50, /* expectedExtraTopMargin= */ 0,
+ /* expectedExtraBottomMargin= */ 0);
+ verify(mViewHost).relayout(mWindowAttrsCaptor.capture());
+ assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+
+ // Window manager should be released (without animation) when eligible becomes false.
+ assertFalse(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ false),
+ mTaskListener, /* canShow= */ true));
+
+ verify(windowManager).release();
+ verify(mOnDismissCallback, never()).run();
+ verify(mAnimationController, never()).startExitAnimation(any(), any());
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
+ public void testUpdateCompatInfo_notEligibleUntilUpdate_createsLayoutAfterUpdate() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
+
+ assertFalse(windowManager.createLayout(/* canShow= */ true));
+ assertNull(windowManager.mLayout);
+
+ assertTrue(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ true),
+ mTaskListener, /* canShow= */ true));
+
+ assertNotNull(windowManager.mLayout);
+ }
+
+ @Test
+ public void testUpdateCompatInfo_canShowFalse_doesNothing() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ false));
+ assertNull(windowManager.mLayout);
+
+ assertTrue(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ true),
+ mTaskListener, /* canShow= */ false));
+
+ assertNull(windowManager.mLayout);
+ verify(mViewHost, never()).relayout(any());
+ }
+
+ @Test
+ public void testUpdateDisplayLayout_updatesLayoutCorrectly() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+ LetterboxEduDialogLayout layout = windowManager.mLayout;
+ assertNotNull(layout);
+
+ int newDisplayCutoutTop = DISPLAY_CUTOUT_TOP + 7;
+ int newDisplayCutoutBottom = DISPLAY_CUTOUT_BOTTOM + 9;
+ windowManager.updateDisplayLayout(createDisplayLayout(
+ Insets.of(DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutTop,
+ DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutBottom)));
+
+ verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ TASK_WIDTH,
+ /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */
+ newDisplayCutoutTop, /* expectedExtraBottomMargin= */ newDisplayCutoutBottom);
+ verify(mViewHost).relayout(mWindowAttrsCaptor.capture());
+ assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+ }
+
+ @Test
+ public void testRelease_animationIsCancelled() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+ windowManager.release();
+
+ verify(mAnimationController).cancelAnimation();
+ }
+
+ private void verifyLayout(LetterboxEduDialogLayout layout, ViewGroup.LayoutParams params,
+ int expectedWidth, int expectedHeight, int expectedExtraTopMargin,
+ int expectedExtraBottomMargin) {
+ assertThat(params.width).isEqualTo(expectedWidth);
+ assertThat(params.height).isEqualTo(expectedHeight);
+ MarginLayoutParams dialogParams =
+ (MarginLayoutParams) layout.getDialogContainer().getLayoutParams();
+ int verticalMargin = (int) mContext.getResources().getDimension(
+ R.dimen.letterbox_education_dialog_margin);
+ assertThat(dialogParams.topMargin).isEqualTo(verticalMargin + expectedExtraTopMargin);
+ assertThat(dialogParams.bottomMargin).isEqualTo(verticalMargin + expectedExtraBottomMargin);
+ }
+
+ private void verifyAndFinishEnterAnimation(LetterboxEduDialogLayout layout) {
+ verify(mAnimationController).startEnterAnimation(eq(layout), mEndCallbackCaptor.capture());
+ mEndCallbackCaptor.getValue().run();
+ }
+
+ private void verifyAndFinishExitAnimation(LetterboxEduDialogLayout layout) {
+ verify(mAnimationController).startExitAnimation(eq(layout), mEndCallbackCaptor.capture());
+ mEndCallbackCaptor.getValue().run();
+ }
+
+ private LetterboxEduWindowManager createWindowManager(boolean eligible) {
+ return createWindowManager(eligible, /* isTaskbarEduShowing= */ false);
+ }
+
+ private LetterboxEduWindowManager createWindowManager(boolean eligible,
+ boolean isTaskbarEduShowing) {
+ LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext,
+ createTaskInfo(eligible), mSyncTransactionQueue, mTaskListener,
+ createDisplayLayout(), mOnDismissCallback, mAnimationController);
+
+ spyOn(windowManager);
+ doReturn(mViewHost).when(windowManager).createSurfaceViewHost();
+ doReturn(isTaskbarEduShowing).when(windowManager).isTaskbarEduShowing();
+
+ return windowManager;
+ }
+
+ private DisplayLayout createDisplayLayout() {
+ return createDisplayLayout(
+ Insets.of(DISPLAY_CUTOUT_HORIZONTAL, DISPLAY_CUTOUT_TOP, DISPLAY_CUTOUT_HORIZONTAL,
+ DISPLAY_CUTOUT_BOTTOM));
+ }
+
+ private DisplayLayout createDisplayLayout(Insets insets) {
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = TASK_WIDTH;
+ displayInfo.logicalHeight = TASK_HEIGHT;
+ displayInfo.displayCutout = new DisplayCutout(
+ insets, null, null, null, null);
+ return new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+ }
+
+ private static TaskInfo createTaskInfo(boolean eligible) {
+ return createTaskInfo(eligible, new Rect(0, 0, TASK_WIDTH, TASK_HEIGHT));
+ }
+
+ private static TaskInfo createTaskInfo(boolean eligible, Rect bounds) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = TASK_ID;
+ taskInfo.topActivityEligibleForLetterboxEducation = eligible;
+ taskInfo.configuration.windowConfiguration.setBounds(bounds);
+ return taskInfo;
+ }
+}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 63b831d..c8cc3526 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -33,6 +33,7 @@
cc_defaults {
name: "libandroidfw_defaults",
+ cpp_std: "gnu++2b",
cflags: [
"-Werror",
"-Wunreachable-code",
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index 921877dc..fac2fa4 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -288,12 +288,12 @@
template <typename TChar>
inline bool operator==(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
- return rhs == lhs;
+ return BasicStringPiece<TChar>(lhs) == rhs;
}
template <typename TChar>
inline bool operator!=(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
- return rhs != lhs;
+ return BasicStringPiece<TChar>(lhs) != rhs;
}
} // namespace android
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index f627a3c..01b956c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -152,8 +152,8 @@
ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- AChoreographer_postExtendedFrameCallback(
- mRenderThread->mChoreographer, RenderThread::extendedFrameCallback, mRenderThread);
+ AChoreographer_postVsyncCallback(mRenderThread->mChoreographer,
+ RenderThread::extendedFrameCallback, mRenderThread);
}
virtual void drainPendingEvents() override {
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 9039011..858f85f 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.bluetoothmidiservice"
>
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+ <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="true"/>
diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
index 5a900c7..99dcaaa 100644
--- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
@@ -19,7 +19,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.bluetoothmidiservice"
>
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+ <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
<application
android:label="BluetoothMidi"
android:defaultToDeviceProtectedStorage="true"
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index e22580d..21c5029 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -40,10 +40,9 @@
return AChoreographer_routePostFrameCallbackDelayed64(choreographer, callback, data,
delayMillis);
}
-void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
- AChoreographer_extendedFrameCallback callback,
- void* data) {
- return AChoreographer_routePostExtendedFrameCallback(choreographer, callback, data);
+void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
+ AChoreographer_vsyncCallback callback, void* data) {
+ return AChoreographer_routePostVsyncCallback(choreographer, callback, data);
}
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
@@ -71,10 +70,10 @@
const AChoreographerFrameCallbackData* data, size_t index) {
return AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(data, index);
}
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
const AChoreographerFrameCallbackData* data, size_t index) {
- return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(data,
- index);
+ return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos(
+ data, index);
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
const AChoreographerFrameCallbackData* data, size_t index) {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 67a98a9..4aa5bbf 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -29,12 +29,12 @@
AChoreographer_postFrameCallbackDelayed64; # introduced=29
AChoreographer_registerRefreshRateCallback; # introduced=30
AChoreographer_unregisterRefreshRateCallback; # introduced=30
- AChoreographer_postExtendedFrameCallback; # introduced=33
+ AChoreographer_postVsyncCallback; # introduced=33
AChoreographerFrameCallbackData_getFrameTimeNanos; # introduced=33
AChoreographerFrameCallbackData_getFrameTimelinesLength; # introduced=33
AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # introduced=33
AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # introduced=33
- AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos; # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos; # introduced=33
AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos; # introduced=33
AConfiguration_copy;
AConfiguration_delete;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1df326a..f0b180e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -658,6 +658,9 @@
<!-- Permission required for CTS test - CaptioningManagerTest -->
<uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" />
+ <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+ <uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b1cfb11..8cc9e00 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -112,6 +112,7 @@
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
+ "com.google.android.material_material",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
"iconloader_base",
diff --git a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
index 9891156..a751f58 100644
--- a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
+++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
@@ -15,6 +15,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@android:color/system_neutral1_800" />
+ <solid android:color="@color/material_dynamic_neutral20" />
<corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
</shape>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
index ff7cbae..f7b0982 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<color xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@android:color/system_accent1_500" />
+ android:color="@color/material_dynamic_primary50" />
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
index 3a228d5..e1b99ce 100644
--- a/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
+++ b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
@@ -30,7 +30,7 @@
android:radius="28dp"/>
<size
android:height="64dp"/>
- <solid android:color="@*android:color/system_accent1_200" />
+ <solid android:color="@color/material_dynamic_primary80" />
</shape>
</clip>
</item>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index ab600b3..2567176 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -27,11 +27,11 @@
android:layout_gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="14sp"
- android:textColor="@android:color/system_neutral1_900"/>
+ android:textColor="@color/material_dynamic_neutral10"/>
<TextView
android:id="@+id/screen_recording_dialog_source_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@android:color/system_neutral2_700"/>
+ android:textColor="@color/material_dynamic_neutral_variant30"/>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index f1e93c2..4b96d5d 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -33,7 +33,7 @@
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
- <color name="notif_pill_text">@android:color/system_neutral1_50</color>
+ <color name="notif_pill_text">@color/material_dynamic_neutral95</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
@@ -66,15 +66,15 @@
<color name="media_divider">#85ffffff</color>
<!-- media output dialog-->
- <color name="media_dialog_background">@android:color/system_neutral1_900</color>
- <color name="media_dialog_active_item_main_content">@android:color/system_neutral1_900</color>
- <color name="media_dialog_inactive_item_main_content">@android:color/system_neutral1_900</color>
- <color name="media_dialog_item_status">@android:color/system_neutral1_900</color>
- <color name="media_dialog_item_background">@android:color/system_accent2_50</color>
+ <color name="media_dialog_background">@color/material_dynamic_neutral10</color>
+ <color name="media_dialog_active_item_main_content">@color/material_dynamic_neutral10</color>
+ <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_neutral10</color>
+ <color name="media_dialog_item_status">@color/material_dynamic_neutral10</color>
+ <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
<!-- Biometric dialog colors -->
<color name="biometric_dialog_gray">#ffcccccc</color>
- <color name="biometric_dialog_accent">@android:color/system_accent1_300</color>
+ <color name="biometric_dialog_accent">@color/material_dynamic_primary70</color>
<color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
<!-- UDFPS colors -->
@@ -99,5 +99,5 @@
<!-- Accessibility floating menu -->
<color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
- <color name="people_tile_background">@android:color/system_accent2_800</color>
+ <color name="people_tile_background">@color/material_dynamic_secondary20</color>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index faf518e..f4e7cf3 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -87,7 +87,7 @@
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
<color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
- <color name="user_switcher_fullscreen_bg">@android:color/system_neutral1_900</color>
+ <color name="user_switcher_fullscreen_bg">@color/material_dynamic_neutral10</color>
<color name="user_switcher_fullscreen_popup_item_tint">@*android:color/text_color_primary_device_default_dark</color>
<!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* -->
@@ -112,7 +112,7 @@
<!-- Chosen so fill over background matches single tone -->
<color name="dark_mode_qs_icon_color_dual_tone_fill">#99000000</color>
- <color name="notif_pill_text">@android:color/system_neutral1_900</color>
+ <color name="notif_pill_text">@color/material_dynamic_neutral10</color>
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
@@ -131,7 +131,7 @@
<!-- Biometric dialog colors -->
<color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black -->
<color name="biometric_dialog_gray">#ff757575</color>
- <color name="biometric_dialog_accent">@android:color/system_accent1_600</color>
+ <color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
<!-- UDFPS colors -->
@@ -155,6 +155,7 @@
<color name="GM2_grey_900">#202124</color>
<color name="GM2_red_300">#F28B82</color>
+ <color name="GM2_red_500">#EA4335</color>
<color name="GM2_red_700">#C5221F</color>
<color name="GM2_blue_300">#8AB4F8</color>
@@ -174,11 +175,11 @@
<color name="media_seamless_border">?android:attr/colorAccent</color>
<!-- media output dialog-->
- <color name="media_dialog_background" android:lstar="98">@android:color/system_neutral1_100</color>
- <color name="media_dialog_active_item_main_content">@android:color/system_accent1_900</color>
- <color name="media_dialog_inactive_item_main_content">@android:color/system_accent1_600</color>
- <color name="media_dialog_item_status">@android:color/system_accent1_900</color>
- <color name="media_dialog_item_background">@android:color/system_accent2_50</color>
+ <color name="media_dialog_background" android:lstar="98">@color/material_dynamic_neutral90</color>
+ <color name="media_dialog_active_item_main_content">@color/material_dynamic_primary10</color>
+ <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_primary40</color>
+ <color name="media_dialog_item_status">@color/material_dynamic_primary10</color>
+ <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
<!-- controls -->
<color name="control_primary_text">#E6FFFFFF</color>
@@ -213,7 +214,7 @@
<!-- Wallet screen -->
<color name="wallet_card_border">#33FFFFFF</color>
- <color name="people_tile_background">@android:color/system_accent2_50</color>
+ <color name="people_tile_background">@color/material_dynamic_secondary95</color>
<!-- Internet Dialog -->
<!-- Material next state on color-->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d4cc718..3f80647 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1982,6 +1982,12 @@
<!-- Text for privacy dialog, indicating that an app (or multiple) is using an op on behalf of another [CHAR LIMIT=NONE] -->
<string name="ongoing_privacy_dialog_attribution_text">(through <xliff:g id="application name(s)" example="Maps, and Assistant">%s</xliff:g>)</string>
+ <!-- Text for privacy dialog, displaying just the subattribution label [CHAR LIMIT=NONE] -->
+ <string name="ongoing_privacy_dialog_attribution_label">(<xliff:g id="attribution_label" example="For Wallet">%s</xliff:g>)</string>
+
+ <!-- Text for privacy dialog, displaying both subattribution and proxy label [CHAR LIMIT=NONE] -->
+ <string name="ongoing_privacy_dialog_attribution_proxy_label">(<xliff:g id="attribution_label" example="For Wallet">%1$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Maps, and Assistant">%2$s</xliff:g>)</string>
+
<!-- Text for camera app op [CHAR LIMIT=20]-->
<string name="privacy_type_camera">camera</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 09ae3f9..9d65c38 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -296,7 +296,7 @@
<item name="darkIconTheme">@style/DualToneDarkTheme</item>
<item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
<item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
- <item name="wallpaperTextColorAccent">@*android:color/system_accent1_100</item>
+ <item name="wallpaperTextColorAccent">@color/material_dynamic_primary90</item>
<item name="android:colorError">@*android:color/error_color_material_dark</item>
<item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
<item name="passwordStyle">@style/PasswordTheme</item>
@@ -312,7 +312,7 @@
<style name="Theme.SystemUI.LightWallpaper">
<item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
<item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
- <item name="wallpaperTextColorAccent">@*android:color/system_accent2_600</item>
+ <item name="wallpaperTextColorAccent">@color/material_dynamic_secondary40</item>
<item name="android:colorError">@*android:color/error_color_material_light</item>
<item name="shadowRadius">0</item>
@@ -353,9 +353,9 @@
<item name="android:colorError">@*android:color/error_color_material_dark</item>
<item name="android:windowIsFloating">true</item>
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
- <item name="offStateColor">@android:color/system_neutral1_800</item>
- <item name="underSurfaceColor">@android:color/system_neutral1_1000</item>
- <item name="android:colorBackground">@android:color/system_neutral1_900</item>
+ <item name="offStateColor">@color/material_dynamic_neutral20</item>
+ <item name="underSurfaceColor">@color/material_dynamic_neutral0</item>
+ <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
<item name="android:itemTextAppearance">@style/Control.MenuItem</item>
</style>
@@ -578,7 +578,7 @@
<!-- Media controls always have light background -->
<style name="MediaPlayer" parent="@*android:style/Theme.DeviceDefault.Light">
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:backgroundTint">@android:color/system_accent2_50</item>
+ <item name="android:backgroundTint">@color/material_dynamic_secondary95</item>
</style>
<style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
@@ -887,8 +887,8 @@
</style>
<style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
- <item name="android:colorBackground">@android:color/system_neutral1_900</item>
- <item name="android:itemBackground">@android:color/system_neutral1_800</item>
+ <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
+ <item name="android:itemBackground">@color/material_dynamic_neutral20</item>
<!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index 8bcb7c9..ccbb0c5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -43,6 +43,7 @@
private static final String TAG = "KeyguardEsimArea";
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+ private int mSubscriptionId;
private EuiccManager mEuiccManager;
private BroadcastReceiver mReceiver =
@@ -87,6 +88,10 @@
setOnClickListener(this);
}
+ public void setSubscriptionId(int subscriptionId) {
+ mSubscriptionId = subscriptionId;
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -113,6 +118,12 @@
@Override
public void onClick(View v) {
+ SubscriptionInfo sub = SubscriptionManager.from(mContext)
+ .getActiveSubscriptionInfo(mSubscriptionId);
+ if (sub == null) {
+ Log.e(TAG, "No active subscription with subscriptionId: " + mSubscriptionId);
+ return;
+ }
Intent intent = new Intent(ACTION_DISABLE_ESIM);
intent.setPackage(mContext.getPackageName());
PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
@@ -120,7 +131,7 @@
0 /* requestCode */,
intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED, UserHandle.SYSTEM);
- mEuiccManager
- .switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, callbackIntent);
+ mEuiccManager.switchToSubscription(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, sub.getPortIndex(), callbackIntent);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c0f9ce7..736e34e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -37,8 +37,9 @@
super(context, attrs);
}
- public void setEsimLocked(boolean locked) {
+ public void setEsimLocked(boolean locked, int subscriptionId) {
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
+ esimButton.setSubscriptionId(subscriptionId);
esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index e04bfdc..0394e76 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -103,7 +103,7 @@
showDefaultMessage();
}
- mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+ mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 0730922..ea94b19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -169,6 +169,7 @@
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
+ esimButton.setSubscriptionId(mSubId);
esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
mPasswordEntry.requestFocus();
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 23ca923..ec11065 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -192,6 +192,7 @@
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
Comparator.comparing(Class::getName));
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents());
+ sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
startServicesIfNeeded(
sortedStartables, "StartServices", vendorComponent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 54664f2..b60e266 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -18,15 +18,13 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
-import static java.util.Objects.requireNonNull;
-
import android.content.ClipboardManager;
import android.content.Context;
import android.provider.DeviceConfig;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.TimeoutHandler;
+import com.android.systemui.util.DeviceConfigProxy;
import javax.inject.Inject;
@@ -37,19 +35,24 @@
public class ClipboardListener extends CoreStartable
implements ClipboardManager.OnPrimaryClipChangedListener {
+ private final DeviceConfigProxy mDeviceConfig;
+ private final ClipboardOverlayControllerFactory mOverlayFactory;
+ private final ClipboardManager mClipboardManager;
private ClipboardOverlayController mClipboardOverlayController;
- private ClipboardManager mClipboardManager;
@Inject
- public ClipboardListener(Context context) {
+ public ClipboardListener(Context context, DeviceConfigProxy deviceConfigProxy,
+ ClipboardOverlayControllerFactory overlayFactory, ClipboardManager clipboardManager) {
super(context);
+ mDeviceConfig = deviceConfigProxy;
+ mOverlayFactory = overlayFactory;
+ mClipboardManager = clipboardManager;
}
@Override
public void start() {
- if (DeviceConfig.getBoolean(
+ if (mDeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
- mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
mClipboardManager.addPrimaryClipChangedListener(this);
}
}
@@ -60,8 +63,7 @@
return;
}
if (mClipboardOverlayController == null) {
- mClipboardOverlayController =
- new ClipboardOverlayController(mContext, new TimeoutHandler(mContext));
+ mClipboardOverlayController = mOverlayFactory.create(mContext);
}
mClipboardOverlayController.setClipData(
mClipboardManager.getPrimaryClip(), mClipboardManager.getPrimaryClipSource());
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java
new file mode 100644
index 0000000..e1c11c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.clipboardoverlay;
+
+import android.content.Context;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.TimeoutHandler;
+
+import javax.inject.Inject;
+
+/**
+ * A factory that churns out ClipboardOverlayControllers on demand.
+ */
+@SysUISingleton
+public class ClipboardOverlayControllerFactory {
+
+ @Inject
+ public ClipboardOverlayControllerFactory() {}
+
+ /**
+ * One new ClipboardOverlayController, coming right up!
+ */
+ public ClipboardOverlayController create(Context context) {
+ return new ClipboardOverlayController(context, new TimeoutHandler(context));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index fa23842..f6aeb2a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -49,6 +49,7 @@
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
+import com.android.systemui.clipboardoverlay.ClipboardOverlayControllerFactory;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
@@ -291,4 +292,11 @@
public ModeSwitchesController providesModeSwitchesController(Context context) {
return new ModeSwitchesController(context);
}
+
+ /***/
+ @Provides
+ @SysUISingleton
+ public ClipboardOverlayControllerFactory provideClipboardOverlayControllerFactory() {
+ return new ClipboardOverlayControllerFactory();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b51eb07..bd472a4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -31,6 +31,7 @@
import android.app.role.RoleManager;
import android.app.smartspace.SmartspaceManager;
import android.app.trust.TrustManager;
+import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.om.OverlayManager;
@@ -451,6 +452,12 @@
@Provides
@Singleton
+ static ClipboardManager provideClipboardManager(Context context) {
+ return context.getSystemService(ClipboardManager.class);
+ }
+
+ @Provides
+ @Singleton
static SmartspaceManager provideSmartspaceManager(Context context) {
return context.getSystemService(SmartspaceManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index dd36fd6..5487c42 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -145,7 +145,7 @@
// 900 - media
public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
- public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
+ public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true);
public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index ecaa142..83ad027 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -540,14 +540,14 @@
}
private fun getBackgroundColor(): Int {
- return context.getColor(android.R.color.system_accent2_50)
+ return context.getColor(R.color.material_dynamic_secondary95)
}
private fun getForegroundColor(): Int {
return if (mediaFlags.useMediaSessionLayout()) {
- context.getColor(android.R.color.system_neutral2_200)
+ context.getColor(R.color.material_dynamic_neutral_variant80)
} else {
- context.getColor(android.R.color.system_accent2_900)
+ context.getColor(R.color.material_dynamic_secondary10)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index ce7a697..831a606 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -476,7 +476,7 @@
appIconView.clearColorFilter();
if (data.getAppIcon() != null && !data.getResumption()) {
appIconView.setImageIcon(data.getAppIcon());
- int color = mContext.getColor(android.R.color.system_accent2_900);
+ int color = mContext.getColor(R.color.material_dynamic_secondary10);
appIconView.setColorFilter(color);
} else {
// Resume players use launcher icon
@@ -590,7 +590,7 @@
} else {
appIconView.setImageResource(R.drawable.ic_music_note);
}
- int color = mContext.getColor(android.R.color.system_accent2_900);
+ int color = mContext.getColor(R.color.material_dynamic_secondary10);
appIconView.setColorFilter(color);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index e1ff110..1d1a230 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -150,7 +150,7 @@
private val themeText = com.android.settingslib.Utils.getColorAttr(context,
com.android.internal.R.attr.textColorPrimary).defaultColor
- private val bgColor = context.getColor(android.R.color.system_accent2_50)
+ private val bgColor = context.getColor(R.color.material_dynamic_secondary95)
// Internal listeners are part of the internal pipeline. External listeners (those registered
// with [MediaDeviceManager.addListener]) receive events after they have propagated through
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 2dff947..c3b4354 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,12 +16,7 @@
package com.android.systemui.media.dagger;
-import android.content.Context;
-import android.os.Handler;
-import android.view.WindowManager;
-
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaFlags;
import com.android.systemui.media.MediaHierarchyManager;
@@ -34,11 +29,8 @@
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.commandline.CommandRegistry;
import java.util.Optional;
-import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -101,13 +93,11 @@
@SysUISingleton
static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
MediaTttFlags mediaTttFlags,
- CommandQueue commandQueue,
- Context context,
- WindowManager windowManager) {
+ Lazy<MediaTttChipControllerSender> controllerSenderLazy) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager));
+ return Optional.of(controllerSenderLazy.get());
}
/** */
@@ -115,16 +105,11 @@
@SysUISingleton
static Optional<MediaTttChipControllerReceiver> providesMediaTttChipControllerReceiver(
MediaTttFlags mediaTttFlags,
- CommandQueue commandQueue,
- Context context,
- WindowManager windowManager,
- @Main Handler mainHandler) {
+ Lazy<MediaTttChipControllerReceiver> controllerReceiverLazy) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(
- new MediaTttChipControllerReceiver(
- commandQueue, context, windowManager, mainHandler));
+ return Optional.of(controllerReceiverLazy.get());
}
/** */
@@ -132,14 +117,11 @@
@SysUISingleton
static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
MediaTttFlags mediaTttFlags,
- CommandRegistry commandRegistry,
- Context context,
- @Main Executor mainExecutor) {
+ Lazy<MediaTttCommandLineHelper> helperLazy) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(
- new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor));
+ return Optional.of(helperLazy.get());
}
/** */
@@ -147,13 +129,12 @@
@SysUISingleton
static Optional<MediaMuteAwaitConnectionCli> providesMediaMuteAwaitConnectionCli(
MediaFlags mediaFlags,
- CommandRegistry commandRegistry,
- Context context
+ Lazy<MediaMuteAwaitConnectionCli> muteAwaitConnectionCliLazy
) {
if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaMuteAwaitConnectionCli(commandRegistry, context));
+ return Optional.of(muteAwaitConnectionCliLazy.get());
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 4993105..ee2fba0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -25,8 +25,11 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
import com.android.internal.widget.CachingIconView
import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
/**
* A superclass controller that provides common functionality for showing chips on the sender device
@@ -38,6 +41,7 @@
abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
internal val context: Context,
private val windowManager: WindowManager,
+ @Main private val mainExecutor: DelayableExecutor,
@LayoutRes private val chipLayoutRes: Int
) {
/** The window layout parameters we'll use when attaching the view to a window. */
@@ -56,6 +60,9 @@
/** The chip view currently being displayed. Null if the chip is not being displayed. */
var chipView: ViewGroup? = null
+ /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
+ var cancelChipViewTimeout: Runnable? = null
+
/**
* Displays the chip with the current state.
*
@@ -77,8 +84,11 @@
if (oldChipView == null) {
windowManager.addView(chipView, windowLayoutParams)
}
- }
+ // Cancel and re-set the chip timeout each time we get a new state.
+ cancelChipViewTimeout?.run()
+ cancelChipViewTimeout = mainExecutor.executeDelayed(this::removeChip, TIMEOUT_MILLIS)
+ }
/** Hides the chip. */
fun removeChip() {
@@ -118,3 +128,5 @@
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
// UpdateMediaTapToTransferReceiverDisplayTest
private const val WINDOW_TITLE = "Media Transfer Chip View"
+@VisibleForTesting
+const val TIMEOUT_MILLIS = 3000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 18623c0..214a888 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -18,7 +18,6 @@
import android.app.StatusBarManager
import android.content.Context
-import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
import android.os.Handler
@@ -27,10 +26,10 @@
import android.view.WindowManager
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
/**
@@ -43,9 +42,10 @@
commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
+ mainExecutor: DelayableExecutor,
@Main private val mainHandler: Handler,
) : MediaTttChipControllerCommon<ChipStateReceiver>(
- context, windowManager, R.layout.media_ttt_chip_receiver
+ context, windowManager, mainExecutor, R.layout.media_ttt_chip_receiver
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferReceiverDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index da767ea..482e604 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -27,8 +27,10 @@
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
/**
@@ -40,8 +42,9 @@
commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
+ @Main private val mainExecutor: DelayableExecutor,
) : MediaTttChipControllerCommon<ChipStateSender>(
- context, windowManager, R.layout.media_ttt_chip
+ context, windowManager, mainExecutor, R.layout.media_ttt_chip
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferSenderDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9199911..9ea2763 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -263,15 +263,6 @@
// Notify FalsingManager that an intentional gesture has occurred.
// TODO(b/186519446): use a different method than isFalseTouch
mFalsingManager.isFalseTouch(BACK_GESTURE);
- // Only inject back keycodes when ahead-of-time back dispatching is disabled.
- if (mBackAnimation == null) {
- boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, "Triggered back: down="
- + sendDown + ", up=" + sendUp);
- }
- }
mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
(int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 23482677..fe4cb71 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -17,6 +17,7 @@
package com.android.systemui.privacy
import android.content.Context
+import android.content.Intent
import android.graphics.drawable.LayerDrawable
import android.os.Bundle
import android.text.TextUtils
@@ -40,12 +41,12 @@
* @param context A context to create the dialog
* @param list list of elements to show in the dialog. The elements will show in the same order they
* appear in the list
- * @param activityStarter a callback to start an activity for a given package name and user id
+ * @param activityStarter a callback to start an activity for a given package name, user id, attributionTag and intent
*/
class PrivacyDialog(
context: Context,
private val list: List<PrivacyElement>,
- activityStarter: (String, Int) -> Unit
+ activityStarter: (String, Int, CharSequence?, Intent?) -> Unit
) : SystemUIDialog(context, R.style.PrivacyDialog) {
private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>()
@@ -118,13 +119,7 @@
app
}
val firstLine = context.getString(stringId, appName)
- val finalText = element.attribution?.let {
- TextUtils.concat(
- firstLine,
- " ",
- context.getString(R.string.ongoing_privacy_dialog_attribution_text, it)
- )
- } ?: firstLine
+ val finalText = getFinalText(firstLine, element.attributionLabel, element.proxyLabel)
newView.requireViewById<TextView>(R.id.text).text = finalText
if (element.phoneCall) {
newView.requireViewById<View>(R.id.chevron).visibility = View.GONE
@@ -138,6 +133,25 @@
return newView
}
+ private fun getFinalText(
+ firstLine: CharSequence,
+ attributionLabel: CharSequence?,
+ proxyLabel: CharSequence?
+ ): CharSequence {
+ var dialogText: CharSequence? = null
+ if (attributionLabel != null && proxyLabel != null) {
+ dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_proxy_label,
+ attributionLabel, proxyLabel)
+ } else if (attributionLabel != null) {
+ dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_label,
+ attributionLabel)
+ } else if (proxyLabel != null) {
+ dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_text,
+ proxyLabel)
+ }
+ return if (dialogText != null) TextUtils.concat(firstLine, " ", dialogText) else firstLine
+ }
+
private fun getStringIdForState(active: Boolean): Int {
return if (active) {
R.string.ongoing_privacy_dialog_using_op
@@ -157,7 +171,8 @@
private val clickListener = View.OnClickListener { v ->
v.tag?.let {
val element = it as PrivacyElement
- activityStarter(element.packageName, element.userId)
+ activityStarter(element.packageName, element.userId,
+ element.attributionTag, element.navigationIntent)
}
}
@@ -167,11 +182,15 @@
val packageName: String,
val userId: Int,
val applicationName: CharSequence,
- val attribution: CharSequence?,
+ val attributionTag: CharSequence?,
+ val attributionLabel: CharSequence?,
+ val proxyLabel: CharSequence?,
val lastActiveTimestamp: Long,
val active: Boolean,
val enterprise: Boolean,
- val phoneCall: Boolean
+ val phoneCall: Boolean,
+ val permGroupName: CharSequence,
+ val navigationIntent: Intent?
) {
private val builder = StringBuilder("PrivacyElement(")
@@ -180,8 +199,14 @@
builder.append(", packageName=$packageName")
builder.append(", userId=$userId")
builder.append(", appName=$applicationName")
- if (attribution != null) {
- builder.append(", attribution=$attribution")
+ if (attributionTag != null) {
+ builder.append(", attributionTag=$attributionTag")
+ }
+ if (attributionLabel != null) {
+ builder.append(", attributionLabel=$attributionLabel")
+ }
+ if (proxyLabel != null) {
+ builder.append(", proxyLabel=$proxyLabel")
}
builder.append(", lastActive=$lastActiveTimestamp")
if (active) {
@@ -193,7 +218,10 @@
if (phoneCall) {
builder.append(", phoneCall")
}
- builder.append(")")
+ builder.append(", permGroupName=$permGroupName)")
+ if (navigationIntent != null) {
+ builder.append(", navigationIntent=$navigationIntent")
+ }
}
override fun toString(): String = builder.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index d0aa710..6107123 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -19,11 +19,12 @@
import android.Manifest
import android.app.ActivityManager
import android.app.Dialog
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.UserHandle
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
import android.permission.PermissionManager
import android.util.Log
import androidx.annotation.MainThread
@@ -45,11 +46,12 @@
override fun makeDialog(
context: Context,
list: List<PrivacyDialog.PrivacyElement>,
- starter: (String, Int) -> Unit
+ starter: (String, Int, CharSequence?, Intent?) -> Unit
): PrivacyDialog {
return PrivacyDialog(context, list, starter)
}
}
+
/**
* Controller for [PrivacyDialog].
*
@@ -115,12 +117,19 @@
}
@MainThread
- private fun startActivity(packageName: String, userId: Int) {
- val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
- intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ private fun startActivity(
+ packageName: String,
+ userId: Int,
+ attributionTag: CharSequence?,
+ navigationIntent: Intent?
+ ) {
+ val intent = if (navigationIntent == null) {
+ getDefaultManageAppPermissionsIntent(packageName, userId)
+ } else {
+ navigationIntent
+ }
uiEventLogger.log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
- userId, packageName)
+ userId, packageName)
privacyLogger.logStartSettingsActivityFromDialog(packageName, userId)
if (!keyguardStateController.isUnlocked) {
// If we are locked, hide the dialog so the user can unlock
@@ -137,7 +146,39 @@
}
@WorkerThread
- private fun permGroupUsage(): List<PermGroupUsage> {
+ private fun getManagePermissionIntent(
+ packageName: String,
+ userId: Int,
+ permGroupName: CharSequence,
+ attributionTag: CharSequence?
+ ): Intent
+ {
+ lateinit var intent: Intent
+ if (attributionTag != null) {
+ intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE)
+ intent.setPackage(packageName)
+ intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName.toString())
+ intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, arrayOf(attributionTag.toString()))
+ intent.putExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, true)
+ val resolveInfo = packageManager.resolveActivity(
+ intent, PackageManager.ResolveInfoFlags.of(0))
+ ?: return getDefaultManageAppPermissionsIntent(packageName, userId)
+ intent.component = ComponentName(packageName, resolveInfo.activityInfo.name)
+ return intent
+ } else {
+ return getDefaultManageAppPermissionsIntent(packageName, userId)
+ }
+ }
+
+ fun getDefaultManageAppPermissionsIntent(packageName: String, userId: Int): Intent {
+ val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ return intent
+ }
+
+ @WorkerThread
+ private fun permGroupUsage(): List<PermissionGroupUsage> {
return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
}
@@ -160,7 +201,7 @@
val userInfos = userTracker.userProfiles
privacyLogger.logUnfilteredPermGroupUsage(usage)
val items = usage.mapNotNull {
- val type = filterType(permGroupToPrivacyType(it.permGroupName))
+ val type = filterType(permGroupToPrivacyType(it.permissionGroupName))
val userInfo = userInfos.firstOrNull { ui -> ui.id == UserHandle.getUserId(it.uid) }
if (userInfo != null || it.isPhoneCall) {
type?.let { t ->
@@ -170,17 +211,24 @@
} else {
getLabelForPackage(it.packageName, it.uid)
}
+ val userId = UserHandle.getUserId(it.uid)
PrivacyDialog.PrivacyElement(
t,
it.packageName,
- UserHandle.getUserId(it.uid),
+ userId,
appName,
- it.attribution,
- it.lastAccess,
+ it.attributionTag,
+ it.attributionLabel,
+ it.proxyLabel,
+ it.lastAccessTimeMillis,
it.isActive,
// If there's no user info, we're in a phoneCall in secondary user
userInfo?.isManagedProfile ?: false,
- it.isPhoneCall
+ it.isPhoneCall,
+ it.permissionGroupName,
+ getManagePermissionIntent(it.packageName, userId,
+ it.permissionGroupName,
+ it.attributionTag)
)
}
} else {
@@ -215,8 +263,8 @@
private fun getLabelForPackage(packageName: String, uid: Int): CharSequence {
return try {
packageManager
- .getApplicationInfoAsUser(packageName, 0, UserHandle.getUserId(uid))
- .loadLabel(packageManager)
+ .getApplicationInfoAsUser(packageName, 0, UserHandle.getUserId(uid))
+ .loadLabel(packageManager)
} catch (_: PackageManager.NameNotFoundException) {
Log.w(TAG, "Label not found for: $packageName")
packageName
@@ -235,7 +283,7 @@
private fun filterType(type: PrivacyType?): PrivacyType? {
return type?.let {
if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
- privacyItemController.micCameraAvailable) {
+ privacyItemController.micCameraAvailable) {
it
} else if (it == PrivacyType.TYPE_LOCATION && privacyItemController.locationAvailable) {
it
@@ -278,7 +326,7 @@
fun makeDialog(
context: Context,
list: List<PrivacyDialog.PrivacyElement>,
- starter: (String, Int) -> Unit
+ starter: (String, Int, CharSequence?, Intent?) -> Unit
): PrivacyDialog
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index 7c82a82..1a268b5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -16,7 +16,7 @@
package com.android.systemui.privacy.logging
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogMessage
@@ -100,7 +100,7 @@
})
}
- fun logUnfilteredPermGroupUsage(contents: List<PermGroupUsage>) {
+ fun logUnfilteredPermGroupUsage(contents: List<PermissionGroupUsage>) {
log(LogLevel.DEBUG, {
str1 = contents.toString()
}, {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 3b305bb..8afb793 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -1,8 +1,16 @@
package com.android.systemui.qs
+import android.content.Intent
+import android.permission.PermissionGroupUsage
+import android.permission.PermissionManager
+import android.provider.DeviceConfig
import android.view.View
+import androidx.annotation.WorkerThread
import com.android.internal.R
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyChipEvent
import com.android.systemui.privacy.PrivacyDialogController
@@ -10,7 +18,11 @@
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.DeviceConfigProxy
+import java.util.concurrent.Executor
import javax.inject.Inject
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
interface ChipVisibilityListener {
fun onChipVisibilityRefreshed(visible: Boolean)
@@ -32,18 +44,46 @@
private val privacyChip: OngoingPrivacyChip,
private val privacyDialogController: PrivacyDialogController,
private val privacyLogger: PrivacyLogger,
- private val iconContainer: StatusIconContainer
+ private val iconContainer: StatusIconContainer,
+ private val permissionManager: PermissionManager,
+ @Background private val backgroundExecutor: Executor,
+ @Main private val uiExecutor: Executor,
+ private val activityStarter: ActivityStarter,
+ private val appOpsController: AppOpsController,
+ private val deviceConfigProxy: DeviceConfigProxy
) {
+ companion object {
+ const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+ }
+
var chipVisibilityListener: ChipVisibilityListener? = null
+
private var listening = false
private var micCameraIndicatorsEnabled = false
private var locationIndicatorsEnabled = false
private var privacyChipLogged = false
+ private var safetyCenterEnabled = false
private val cameraSlot = privacyChip.resources.getString(R.string.status_bar_camera)
private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
+ private val devicePropertiesChangedListener =
+ object : DeviceConfig.OnPropertiesChangedListener {
+ override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+ safetyCenterEnabled = properties.getBoolean(SAFETY_CENTER_ENABLED, false)
+ }
+ }
+
+ init {
+ safetyCenterEnabled = deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_CENTER_ENABLED, false)
+ deviceConfigProxy.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ uiExecutor,
+ devicePropertiesChangedListener)
+ }
+
private val picCallback: PrivacyItemController.Callback =
object : PrivacyItemController.Callback {
override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
@@ -77,7 +117,11 @@
privacyChip.setOnClickListener {
// If the privacy chip is visible, it means there were some indicators
uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
- privacyDialogController.showDialog(privacyChip.context)
+ if (safetyCenterEnabled) {
+ showSafetyHub()
+ } else {
+ privacyDialogController.showDialog(privacyChip.context)
+ }
}
setChipVisibility(privacyChip.visibility == View.VISIBLE)
micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
@@ -87,6 +131,26 @@
updatePrivacyIconSlots()
}
+ private fun showSafetyHub() {
+ backgroundExecutor.execute {
+ val usage = ArrayList(permGroupUsage())
+ privacyLogger.logUnfilteredPermGroupUsage(usage)
+ val startSafetyHub = Intent(Intent.ACTION_VIEW_SAFETY_HUB)
+ startSafetyHub.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
+ usage)
+ startSafetyHub.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ uiExecutor.execute {
+ activityStarter.startActivity(startSafetyHub, true,
+ ActivityLaunchAnimator.Controller.fromView(privacyChip))
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun permGroupUsage(): List<PermissionGroupUsage> {
+ return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
+ }
+
fun onParentInvisible() {
chipVisibilityListener = null
privacyChip.setOnClickListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 17f85ee..3c7933f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -381,14 +381,17 @@
: View.INVISIBLE);
mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|| (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
- boolean footerVisible = !mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
+ boolean qsPanelVisible = !mQsDisabled && expandVisually;
+ boolean footerVisible = qsPanelVisible && (expanded || !keyguardShowing || mHeaderAnimating
|| mShowCollapsedOnKeyguard);
mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
mQSFooterActionController.setVisible(footerVisible);
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|| (expanded && !mStackScrollerOverscrolling));
- mQSPanelController.setVisibility(
- !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
+ mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
+ if (DEBUG) {
+ Log.d(TAG, "Footer: " + footerVisible + ", QS Panel: " + qsPanelVisible);
+ }
}
private boolean isKeyguardState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 3a3f581..952cd9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -202,10 +202,12 @@
float newHeight = state.height;
float newNotificationEnd = newYTranslation + newHeight;
boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned();
+ final boolean shadeClosedWithHUN =
+ ambientState.isShadeOpening() && !ambientState.isShadeExpanded();
if (mClipNotificationScrollToTop
&& (!state.inShelf || (isHeadsUp && !firstHeadsUp))
&& newYTranslation < clipStart
- && !ambientState.isShadeOpening()) {
+ && shadeClosedWithHUN) {
// The previous view is overlapping on top, clip!
float overlapAmount = clipStart - newYTranslation;
state.clipTopAmount = (int) overlapAmount;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index 5e9bae9..d2142dc 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -48,8 +48,8 @@
R.dimen.card_carousel_dot_selected_radius);
mDotMargin = context.getResources().getDimensionPixelSize(R.dimen.card_carousel_dot_margin);
- mUnselectedColor = context.getColor(com.android.internal.R.color.system_neutral1_300);
- mSelectedColor = context.getColor(com.android.internal.R.color.system_neutral1_0);
+ mUnselectedColor = context.getColor(R.color.material_dynamic_neutral70);
+ mSelectedColor = context.getColor(R.color.material_dynamic_neutral100);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 9917777..92e7f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -278,7 +278,7 @@
private Drawable getHomeIndicatorDrawable() {
Drawable drawable = getDrawable(R.drawable.ic_close);
- drawable.setTint(getColor(com.android.internal.R.color.system_neutral1_300));
+ drawable.setTint(getColor(R.color.material_dynamic_neutral70));
return drawable;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
new file mode 100644
index 0000000..e15b6cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.clipboardoverlay;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.provider.DeviceConfig;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.DeviceConfigProxyFake;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ClipboardListenerTest extends SysuiTestCase {
+
+ @Mock
+ private ClipboardManager mClipboardManager;
+ @Mock
+ private ClipboardOverlayControllerFactory mClipboardOverlayControllerFactory;
+ @Mock
+ private ClipboardOverlayController mOverlayController;
+ private DeviceConfigProxyFake mDeviceConfigProxy;
+
+ private ClipData mSampleClipData;
+ private String mSampleSource = "Example source";
+
+ @Captor
+ private ArgumentCaptor<Runnable> mRunnableCaptor;
+ @Captor
+ private ArgumentCaptor<ClipData> mClipDataCaptor;
+ @Captor
+ private ArgumentCaptor<String> mStringCaptor;
+
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mClipboardOverlayControllerFactory.create(any())).thenReturn(
+ mOverlayController);
+ when(mClipboardManager.hasPrimaryClip()).thenReturn(true);
+
+
+ mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
+ new ClipData.Item("Test Item"));
+ when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData);
+ when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
+
+ mDeviceConfigProxy = new DeviceConfigProxyFake();
+ }
+
+ @Test
+ public void test_disabled() {
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+ "false", false);
+ ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+ mClipboardOverlayControllerFactory, mClipboardManager);
+ listener.start();
+ verifyZeroInteractions(mClipboardManager);
+ }
+
+ @Test
+ public void test_enabled() {
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+ "true", false);
+ ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+ mClipboardOverlayControllerFactory, mClipboardManager);
+ listener.start();
+ verify(mClipboardManager).addPrimaryClipChangedListener(any());
+ }
+
+ @Test
+ public void test_consecutiveCopies() {
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+ "true", false);
+ ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+ mClipboardOverlayControllerFactory, mClipboardManager);
+ listener.start();
+ listener.onPrimaryClipChanged();
+
+ verify(mClipboardOverlayControllerFactory).create(any());
+
+ verify(mOverlayController).setClipData(mClipDataCaptor.capture(), mStringCaptor.capture());
+
+ assertEquals(mSampleClipData, mClipDataCaptor.getValue());
+ assertEquals(mSampleSource, mStringCaptor.getValue());
+
+ verify(mOverlayController).setOnSessionCompleteListener(mRunnableCaptor.capture());
+
+ // Should clear the overlay controller
+ mRunnableCaptor.getValue().run();
+
+ listener.onPrimaryClipChanged();
+
+ verify(mClipboardOverlayControllerFactory, times(2)).create(any());
+
+ // Not calling the runnable here, just change the clip again and verify that the overlay is
+ // NOT recreated.
+
+ listener.onPrimaryClipChanged();
+
+ verify(mClipboardOverlayControllerFactory, times(2)).create(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 14afece..794bc09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -39,7 +39,6 @@
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -49,7 +48,6 @@
import java.util.concurrent.Executor
@SmallTest
-@Ignore("b/216286227")
class MediaTttCommandLineHelperTest : SysuiTestCase() {
private val inlineExecutor = Executor { command -> command.run() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index f05d621..ea0a5a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.graphics.drawable.Drawable
-import android.graphics.drawable.Icon
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
@@ -26,10 +25,13 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
@@ -39,10 +41,12 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@Ignore("b/216286227")
class MediaTttChipControllerCommonTest : SysuiTestCase() {
private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
+ private lateinit var fakeClock: FakeSystemClock
+ private lateinit var fakeExecutor: FakeExecutor
+
private lateinit var appIconDrawable: Drawable
@Mock
private lateinit var windowManager: WindowManager
@@ -50,8 +54,11 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
- controllerCommon = TestControllerCommon(context, windowManager)
+ appIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+ fakeClock = FakeSystemClock()
+ fakeExecutor = FakeExecutor(fakeClock)
+
+ controllerCommon = TestControllerCommon(context, windowManager, fakeExecutor)
}
@Test
@@ -71,6 +78,58 @@
}
@Test
+ fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
+ controllerCommon.displayChip(getState())
+ reset(windowManager)
+
+ fakeClock.advanceTime(TIMEOUT_MILLIS - 1)
+
+ verify(windowManager, never()).removeView(any())
+ }
+
+ @Test
+ fun displayChip_chipDisappearsAfterTimeout() {
+ controllerCommon.displayChip(getState())
+ reset(windowManager)
+
+ fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
+ // First, display the chip
+ controllerCommon.displayChip(getState())
+
+ // After some time, re-display the chip
+ val waitTime = 1000L
+ fakeClock.advanceTime(waitTime)
+ controllerCommon.displayChip(getState())
+
+ // Wait until the timeout for the first display would've happened
+ fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1)
+
+ // Verify we didn't hide the chip
+ verify(windowManager, never()).removeView(any())
+ }
+
+ @Test
+ fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
+ // First, display the chip
+ controllerCommon.displayChip(getState())
+
+ // After some time, re-display the chip
+ fakeClock.advanceTime(1000L)
+ controllerCommon.displayChip(getState())
+
+ // Ensure we still hide the chip eventually
+ fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
fun removeChip_chipRemoved() {
// First, add the chip
controllerCommon.displayChip(getState())
@@ -93,10 +152,10 @@
controllerCommon.displayChip(getState())
val chipView = getChipView()
- val state = MediaTttChipState(PACKAGE_NAME)
+ val state = TestChipState(PACKAGE_NAME)
controllerCommon.setIcon(state, chipView)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
assertThat(chipView.getAppIconView().contentDescription)
.isEqualTo(state.getAppName(context))
}
@@ -113,13 +172,18 @@
inner class TestControllerCommon(
context: Context,
- windowManager: WindowManager
- ) : MediaTttChipControllerCommon<MediaTttChipState>(
- context, windowManager, R.layout.media_ttt_chip
+ windowManager: WindowManager,
+ @Main mainExecutor: DelayableExecutor,
+ ) : MediaTttChipControllerCommon<MediaTttChipState>(
+ context, windowManager, mainExecutor, R.layout.media_ttt_chip
) {
override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
}
}
+
+ inner class TestChipState(appPackageName: String?) : MediaTttChipState(appPackageName) {
+ override fun getAppIcon(context: Context) = appIconDrawable
+ }
}
private const val PACKAGE_NAME = "com.android.systemui"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 44f691c..117a6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -17,7 +17,9 @@
package com.android.systemui.media.taptotransfer.receiver
import android.app.StatusBarManager
-import android.graphics.drawable.Icon
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
import android.os.Handler
import android.view.View
@@ -28,33 +30,54 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
-@Ignore("b/216286227")
class MediaTttChipControllerReceiverTest : SysuiTestCase() {
private lateinit var controllerReceiver: MediaTttChipControllerReceiver
@Mock
+ private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var applicationInfo: ApplicationInfo
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
+ private lateinit var fakeAppIconDrawable: Drawable
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
+ fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+ whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+ whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+ whenever(packageManager.getApplicationInfo(
+ eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+ )).thenReturn(applicationInfo)
+ context.setMockPackageManager(packageManager)
+
controllerReceiver = MediaTttChipControllerReceiver(
- commandQueue, context, windowManager, Handler.getMain())
+ commandQueue,
+ context,
+ windowManager,
+ FakeExecutor(FakeSystemClock()),
+ Handler.getMain()
+ )
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -113,13 +136,12 @@
controllerReceiver.displayChip(state)
- assertThat(getChipView().getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-
+ assertThat(getChipView().getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
}
@Test
fun displayChip_hasAppIconDrawable_iconIsDrawable() {
- val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+ val drawable = context.getDrawable(R.drawable.ic_alarm)!!
val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName")
controllerReceiver.displayChip(state)
@@ -133,13 +155,12 @@
controllerReceiver.displayChip(state)
- assertThat(getChipView().getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(APP_NAME)
}
@Test
fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
- val appName = "FakeAppName"
+ val appName = "Override App Name"
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName)
controllerReceiver.displayChip(state)
@@ -156,6 +177,7 @@
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
}
+private const val APP_NAME = "Fake app name"
private const val PACKAGE_NAME = "com.android.systemui"
private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index dc39893..b440064 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -17,6 +17,9 @@
package com.android.systemui.media.taptotransfer.sender
import android.app.StatusBarManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
import android.view.View
import android.view.WindowManager
@@ -28,32 +31,50 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
-@Ignore("b/216286227")
class MediaTttChipControllerSenderTest : SysuiTestCase() {
private lateinit var controllerSender: MediaTttChipControllerSender
@Mock
+ private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var applicationInfo: ApplicationInfo
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
+ private lateinit var fakeAppIconDrawable: Drawable
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager)
+
+ fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+ whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+ whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+ whenever(packageManager.getApplicationInfo(
+ eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+ )).thenReturn(applicationInfo)
+ context.setMockPackageManager(packageManager)
+
+ controllerSender = MediaTttChipControllerSender(
+ commandQueue, context, windowManager, FakeExecutor(FakeSystemClock())
+ )
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -192,9 +213,8 @@
controllerSender.displayChip(state)
val chipView = getChipView()
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -207,9 +227,8 @@
controllerSender.displayChip(state)
val chipView = getChipView()
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -222,9 +241,8 @@
controllerSender.displayChip(state)
val chipView = getChipView()
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -237,9 +255,8 @@
controllerSender.displayChip(state)
val chipView = getChipView()
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -252,9 +269,8 @@
controllerSender.displayChip(state)
val chipView = getChipView()
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -314,9 +330,8 @@
controllerSender.displayChip(state)
val chipView = getChipView()
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -376,9 +391,8 @@
controllerSender.displayChip(state)
val chipView = getChipView()
- assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -451,15 +465,15 @@
/** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToStartCast() =
- AlmostCloseToStartCast(PACKAGE_NAME, DEVICE_NAME)
+ AlmostCloseToStartCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToEndCast() =
- AlmostCloseToEndCast(PACKAGE_NAME, DEVICE_NAME)
+ AlmostCloseToEndCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverTriggered() =
- TransferToReceiverTriggered(PACKAGE_NAME, DEVICE_NAME)
+ TransferToReceiverTriggered(PACKAGE_NAME, OTHER_DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceTriggered() =
@@ -468,23 +482,24 @@
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
TransferToReceiverSucceeded(
- PACKAGE_NAME, DEVICE_NAME, undoCallback
+ PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
TransferToThisDeviceSucceeded(
- PACKAGE_NAME, DEVICE_NAME, undoCallback
+ PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferFailed() = TransferFailed(PACKAGE_NAME)
}
-private const val DEVICE_NAME = "My Tablet"
+private const val APP_NAME = "Fake app name"
+private const val OTHER_DEVICE_NAME = "My Tablet"
private const val PACKAGE_NAME = "com.android.systemui"
-private val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
+private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
.addFeature("feature")
.setPackageName(PACKAGE_NAME)
.build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 511848d..3508226 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -24,7 +24,7 @@
import android.content.pm.UserInfo
import android.os.Process.SYSTEM_UID
import android.os.UserHandle
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
import android.permission.PermissionManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -68,7 +68,8 @@
private const val ENT_USER_ID = 10
private const val TEST_PACKAGE_NAME = "test package name"
- private const val TEST_ATTRIBUTION = "test attribution"
+ private const val TEST_ATTRIBUTION_TAG = "test attribution tag"
+ private const val TEST_PROXY_LABEL = "test proxy label"
private const val PERM_CAMERA = android.Manifest.permission_group.CAMERA
private const val PERM_MICROPHONE = android.Manifest.permission_group.MICROPHONE
@@ -109,12 +110,12 @@
private val dialogProvider = object : PrivacyDialogController.DialogProvider {
var list: List<PrivacyDialog.PrivacyElement>? = null
- var starter: ((String, Int) -> Unit)? = null
+ var starter: ((String, Int, CharSequence?, Intent?) -> Unit)? = null
override fun makeDialog(
context: Context,
list: List<PrivacyDialog.PrivacyElement>,
- starter: (String, Int) -> Unit
+ starter: (String, Int, CharSequence?, Intent?) -> Unit
): PrivacyDialog {
this.list = list
this.starter = starter
@@ -125,13 +126,11 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
nextUid = 0
-
setUpDefaultMockResponses()
controller = PrivacyDialogController(
- permissionManager,
+ permissionManager,
packageManager,
privacyItemController,
userTracker,
@@ -248,40 +247,54 @@
val usage = createMockPermGroupUsage(
packageName = TEST_PACKAGE_NAME,
uid = generateUidForUser(USER_ID),
- permGroupName = PERM_CAMERA,
- lastAccess = 5L,
+ permissionGroupName = PERM_CAMERA,
+ lastAccessTimeMillis = 5L,
isActive = true,
isPhoneCall = false,
- attribution = TEST_ATTRIBUTION
+ attributionTag = null,
+ proxyLabel = TEST_PROXY_LABEL
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context)
exhaustExecutors()
- val expected = PrivacyDialog.PrivacyElement(
- type = PrivacyType.TYPE_CAMERA,
- packageName = TEST_PACKAGE_NAME,
- userId = USER_ID,
- applicationName = TEST_PACKAGE_NAME,
- attribution = TEST_ATTRIBUTION,
- lastActiveTimestamp = 5L,
- active = true,
- phoneCall = false,
- enterprise = false
- )
- assertThat(dialogProvider.list).containsExactly(expected)
+ dialogProvider.list?.let { list ->
+ assertThat(list.get(0).type).isEqualTo(PrivacyType.TYPE_CAMERA)
+ assertThat(list.get(0).packageName).isEqualTo(TEST_PACKAGE_NAME)
+ assertThat(list.get(0).userId).isEqualTo(USER_ID)
+ assertThat(list.get(0).applicationName).isEqualTo(TEST_PACKAGE_NAME)
+ assertThat(list.get(0).attributionTag).isNull()
+ assertThat(list.get(0).attributionLabel).isNull()
+ assertThat(list.get(0).proxyLabel).isEqualTo(TEST_PROXY_LABEL)
+ assertThat(list.get(0).lastActiveTimestamp).isEqualTo(5L)
+ assertThat(list.get(0).active).isTrue()
+ assertThat(list.get(0).phoneCall).isFalse()
+ assertThat(list.get(0).enterprise).isFalse()
+ assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
+ assertThat(isIntentEqual(list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
+ .isTrue()
+ }
+ }
+
+ private fun isIntentEqual(actual: Intent, expected: Intent): Boolean {
+ return actual.action == expected.action &&
+ actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ==
+ expected.getStringExtra(Intent.EXTRA_PACKAGE_NAME) &&
+ actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle ==
+ expected.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle
}
@Test
fun testTwoElementsDifferentType_sorted() {
val usage_camera = createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_camera",
- permGroupName = PERM_CAMERA
+ permissionGroupName = PERM_CAMERA
)
val usage_microphone = createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_microphone",
- permGroupName = PERM_MICROPHONE
+ permissionGroupName = PERM_MICROPHONE
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_microphone, usage_camera)
@@ -322,12 +335,12 @@
val usage_active = createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_active",
isActive = true,
- lastAccess = 0L
+ lastAccessTimeMillis = 0L
)
val usage_active_moreRecent = createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_active_recent",
isActive = true,
- lastAccess = 1L
+ lastAccessTimeMillis = 1L
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_active, usage_active_moreRecent)
@@ -344,17 +357,17 @@
val usage_recent = createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_recent",
isActive = false,
- lastAccess = 0L
+ lastAccessTimeMillis = 0L
)
val usage_moreRecent = createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_moreRecent",
isActive = false,
- lastAccess = 1L
+ lastAccessTimeMillis = 1L
)
val usage_mostRecent = createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_mostRecent",
isActive = false,
- lastAccess = 2L
+ lastAccessTimeMillis = 2L
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_recent, usage_mostRecent, usage_moreRecent)
@@ -370,13 +383,13 @@
@Test
fun testMicAndCameraDisabled() {
val usage_camera = createMockPermGroupUsage(
- permGroupName = PERM_CAMERA
+ permissionGroupName = PERM_CAMERA
)
val usage_microphone = createMockPermGroupUsage(
- permGroupName = PERM_MICROPHONE
+ permissionGroupName = PERM_MICROPHONE
)
val usage_location = createMockPermGroupUsage(
- permGroupName = PERM_LOCATION
+ permissionGroupName = PERM_LOCATION
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -394,13 +407,13 @@
@Test
fun testLocationDisabled() {
val usage_camera = createMockPermGroupUsage(
- permGroupName = PERM_CAMERA
+ permissionGroupName = PERM_CAMERA
)
val usage_microphone = createMockPermGroupUsage(
- permGroupName = PERM_MICROPHONE
+ permissionGroupName = PERM_MICROPHONE
)
val usage_location = createMockPermGroupUsage(
- permGroupName = PERM_LOCATION
+ permissionGroupName = PERM_LOCATION
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -420,13 +433,13 @@
@Test
fun testAllIndicatorsAvailable() {
val usage_camera = createMockPermGroupUsage(
- permGroupName = PERM_CAMERA
+ permissionGroupName = PERM_CAMERA
)
val usage_microphone = createMockPermGroupUsage(
- permGroupName = PERM_MICROPHONE
+ permissionGroupName = PERM_MICROPHONE
)
val usage_location = createMockPermGroupUsage(
- permGroupName = PERM_LOCATION
+ permissionGroupName = PERM_LOCATION
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -444,13 +457,13 @@
@Test
fun testNoIndicatorsAvailable() {
val usage_camera = createMockPermGroupUsage(
- permGroupName = PERM_CAMERA
+ permissionGroupName = PERM_CAMERA
)
val usage_microphone = createMockPermGroupUsage(
- permGroupName = PERM_MICROPHONE
+ permissionGroupName = PERM_MICROPHONE
)
val usage_location = createMockPermGroupUsage(
- permGroupName = PERM_LOCATION
+ permissionGroupName = PERM_LOCATION
)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -500,7 +513,7 @@
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
verify(activityStarter)
.startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
@@ -518,7 +531,7 @@
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID, null, null)
verify(activityStarter)
.startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
@@ -533,7 +546,7 @@
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
activityStartedCaptor.value.onActivityStarted(ActivityManager.START_DELIVERED_TO_TOP)
@@ -548,7 +561,7 @@
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
activityStartedCaptor.value.onActivityStarted(ActivityManager.START_ABORTED)
@@ -578,7 +591,7 @@
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
USER_ID, TEST_PACKAGE_NAME)
}
@@ -599,6 +612,42 @@
verify(uiEventLogger, times(1)).log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED)
}
+ @Test
+ fun testInvalidAttributionTag() {
+ val usage = createMockPermGroupUsage(
+ packageName = TEST_PACKAGE_NAME,
+ uid = generateUidForUser(USER_ID),
+ permissionGroupName = PERM_CAMERA,
+ lastAccessTimeMillis = 5L,
+ isActive = true,
+ isPhoneCall = false,
+ attributionTag = "INVALID_ATTRIBUTION_TAG",
+ proxyLabel = TEST_PROXY_LABEL
+ )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ dialogProvider.list?.let { list ->
+ assertThat(list.get(0).type).isEqualTo(PrivacyType.TYPE_CAMERA)
+ assertThat(list.get(0).packageName).isEqualTo(TEST_PACKAGE_NAME)
+ assertThat(list.get(0).userId).isEqualTo(USER_ID)
+ assertThat(list.get(0).applicationName).isEqualTo(TEST_PACKAGE_NAME)
+ assertThat(list.get(0).attributionTag).isEqualTo("INVALID_ATTRIBUTION_TAG")
+ assertThat(list.get(0).attributionLabel).isNull()
+ assertThat(list.get(0).proxyLabel).isEqualTo(TEST_PROXY_LABEL)
+ assertThat(list.get(0).lastActiveTimestamp).isEqualTo(5L)
+ assertThat(list.get(0).active).isTrue()
+ assertThat(list.get(0).phoneCall).isFalse()
+ assertThat(list.get(0).enterprise).isFalse()
+ assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
+ assertThat(isIntentEqual(list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
+ .isTrue()
+ }
+ }
+
private fun exhaustExecutors() {
FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
}
@@ -608,9 +657,7 @@
`when`(appOpsController.isMicMuted).thenReturn(false)
`when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
- .thenAnswer {
- FakeApplicationInfo(it.getArgument(0))
- }
+ .thenAnswer { FakeApplicationInfo(it.getArgument(0)) }
`when`(privacyItemController.locationAvailable).thenReturn(true)
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
@@ -636,21 +683,24 @@
private fun createMockPermGroupUsage(
packageName: String = TEST_PACKAGE_NAME,
uid: Int = generateUidForUser(USER_ID),
- permGroupName: String = PERM_CAMERA,
- lastAccess: Long = 0L,
+ permissionGroupName: String = PERM_CAMERA,
+ lastAccessTimeMillis: Long = 0L,
isActive: Boolean = false,
isPhoneCall: Boolean = false,
- attribution: CharSequence? = null
- ): PermGroupUsage {
- val usage = mock(PermGroupUsage::class.java)
+ attributionTag: CharSequence? = null,
+ attributionLabel: CharSequence? = null,
+ proxyLabel: CharSequence? = null
+ ): PermissionGroupUsage {
+ val usage = mock(PermissionGroupUsage::class.java)
`when`(usage.packageName).thenReturn(packageName)
`when`(usage.uid).thenReturn(uid)
- `when`(usage.permGroupName).thenReturn(permGroupName)
- `when`(usage.lastAccess).thenReturn(lastAccess)
+ `when`(usage.permissionGroupName).thenReturn(permissionGroupName)
+ `when`(usage.lastAccessTimeMillis).thenReturn(lastAccessTimeMillis)
`when`(usage.isActive).thenReturn(isActive)
`when`(usage.isPhoneCall).thenReturn(isPhoneCall)
- `when`(usage.attribution).thenReturn(attribution)
-
+ `when`(usage.attributionTag).thenReturn(attributionTag)
+ `when`(usage.attributionLabel).thenReturn(attributionLabel)
+ `when`(usage.proxyLabel).thenReturn(proxyLabel)
return usage
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index 1baaf6b..c714fa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -34,6 +34,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import android.content.Intent
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -43,11 +44,11 @@
companion object {
private const val TEST_PACKAGE_NAME = "test_pkg"
private const val TEST_USER_ID = 0
+ private const val TEST_PERM_GROUP = "test_perm_group"
}
@Mock
- private lateinit var starter: (String, Int) -> Unit
-
+ private lateinit var starter: (String, Int, CharSequence?, Intent?) -> Unit
private lateinit var dialog: PrivacyDialog
@Before
@@ -71,16 +72,20 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
false,
false,
- false
+ false,
+ TEST_PERM_GROUP,
+ null
)
)
dialog = PrivacyDialog(context, list, starter)
dialog.show()
dialog.requireViewById<View>(R.id.privacy_item).callOnClick()
- verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID)
+ verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID, null, null)
}
@Test
@@ -115,10 +120,14 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
true,
false,
- false
+ false,
+ TEST_PERM_GROUP,
+ null
),
PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
@@ -126,10 +135,14 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
false,
false,
- false
+ false,
+ TEST_PERM_GROUP,
+ null
)
)
dialog = PrivacyDialog(context, list, starter)
@@ -145,10 +158,14 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
true,
false,
- false
+ false,
+ TEST_PERM_GROUP,
+ null
)
val list = listOf(element)
@@ -171,10 +188,14 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
false,
false,
- false
+ false,
+ TEST_PERM_GROUP,
+ null
)
val list = listOf(element)
@@ -197,10 +218,14 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
false,
true,
- false
+ false,
+ TEST_PERM_GROUP,
+ null
)
val list = listOf(element)
@@ -219,10 +244,14 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
false,
false,
- true
+ true,
+ TEST_PERM_GROUP,
+ null
)
val list = listOf(element)
@@ -241,10 +270,14 @@
TEST_USER_ID,
"App",
null,
+ null,
+ null,
0L,
false,
false,
- true
+ true,
+ TEST_PERM_GROUP,
+ null
)
val list = listOf(element)
@@ -255,17 +288,21 @@
}
@Test
- fun testAttribution() {
+ fun testProxyLabel() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
TEST_PACKAGE_NAME,
TEST_USER_ID,
"App",
- "attribution",
+ null,
+ null,
+ "proxyLabel",
0L,
false,
false,
- true
+ true,
+ TEST_PERM_GROUP,
+ null
)
val list = listOf(element)
@@ -274,7 +311,65 @@
assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
context.getString(
R.string.ongoing_privacy_dialog_attribution_text,
- element.attribution
+ element.proxyLabel
+ )
+ )
+ }
+
+ @Test
+ fun testSubattribution() {
+ val element = PrivacyDialog.PrivacyElement(
+ PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
+ "App",
+ null,
+ "For subattribution",
+ null,
+ 0L,
+ true,
+ false,
+ false,
+ TEST_PERM_GROUP,
+ null
+ )
+
+ val list = listOf(element)
+ dialog = PrivacyDialog(context, list, starter)
+ dialog.show()
+ assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
+ context.getString(
+ R.string.ongoing_privacy_dialog_attribution_label,
+ element.attributionLabel
+ )
+ )
+ }
+
+ @Test
+ fun testSubattributionAndProxyLabel() {
+ val element = PrivacyDialog.PrivacyElement(
+ PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
+ "App",
+ null,
+ "For subattribution",
+ "proxy label",
+ 0L,
+ true,
+ false,
+ false,
+ TEST_PERM_GROUP,
+ null
+ )
+
+ val list = listOf(element)
+ dialog = PrivacyDialog(context, list, starter)
+ dialog.show()
+ assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
+ context.getString(
+ R.string.ongoing_privacy_dialog_attribution_proxy_label,
+ element.attributionLabel, element.proxyLabel
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index 92743ae5..30a5308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -1,31 +1,45 @@
package com.android.systemui.qs
import android.content.Context
+import android.permission.PermissionManager
+import android.provider.DeviceConfig
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyDialogController
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
+ companion object {
+ const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+ }
+
@Mock
private lateinit var privacyItemController: PrivacyItemController
@Mock
@@ -38,11 +52,20 @@
private lateinit var privacyLogger: PrivacyLogger
@Mock
private lateinit var iconContainer: StatusIconContainer
+ @Mock
+ private lateinit var permissionManager: PermissionManager
+ @Mock
+ private lateinit var backgroundExecutor: Executor
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var appOpsController: AppOpsController
+ private val uiExecutor = FakeExecutor(FakeSystemClock())
+ private lateinit var deviceConfigProxy: DeviceConfigProxy
private lateinit var cameraSlotName: String
private lateinit var microphoneSlotName: String
private lateinit var locationSlotName: String
-
private lateinit var controller: HeaderPrivacyIconsController
@Before
@@ -54,6 +77,7 @@
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
+ deviceConfigProxy = DeviceConfigProxyFake()
controller = HeaderPrivacyIconsController(
privacyItemController,
@@ -61,7 +85,13 @@
privacyChip,
privacyDialogController,
privacyLogger,
- iconContainer
+ iconContainer,
+ permissionManager,
+ backgroundExecutor,
+ uiExecutor,
+ activityStarter,
+ appOpsController,
+ deviceConfigProxy
)
}
@@ -111,18 +141,38 @@
@Test
fun testPrivacyChipClicked() {
+ changeProperty(SAFETY_CENTER_ENABLED, false)
controller.onParentVisible()
val captor = argumentCaptor<View.OnClickListener>()
verify(privacyChip).setOnClickListener(capture(captor))
-
captor.value.onClick(privacyChip)
verify(privacyDialogController).showDialog(any(Context::class.java))
}
+ @Test
+ fun testSafetyCenterFlag() {
+ changeProperty(SAFETY_CENTER_ENABLED, true)
+ controller.onParentVisible()
+ val captor = argumentCaptor<View.OnClickListener>()
+ verify(privacyChip).setOnClickListener(capture(captor))
+ captor.value.onClick(privacyChip)
+ verify(privacyDialogController, never()).showDialog(any(Context::class.java))
+ }
+
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
whenever(privacyItemController.locationAvailable).thenReturn(location)
}
+
+ private fun changeProperty(name: String, value: Boolean?) {
+ deviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ name,
+ value?.toString(),
+ false
+ )
+ uiExecutor.runAllReady()
+ }
}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index e122993..75d9b7e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -275,12 +275,10 @@
if (index >= 0) {
RemoteViews presentation = dataset.getFieldDialogPresentation(index);
if (presentation == null) {
- Slog.w(TAG, "fallback to presentation");
- presentation = dataset.getFieldPresentation(index);
- }
- if (presentation == null) {
- Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
- + "service didn't provide a presentation for it on " + dataset);
+ if (sDebug) {
+ Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
+ + "service didn't provide a presentation for it on " + dataset);
+ }
continue;
}
final View view;
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index b026990..33301b1 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -21,8 +21,6 @@
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
-import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
-import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothAdapter.nameForState;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
@@ -230,18 +228,24 @@
if (DEBUG) Log.i(TAG, "stopScan()");
if (!mScanning) {
- Log.d(TAG, " > not scanning.");
+ if (DEBUG) Log.d(TAG, " > not scanning.");
return;
}
+ // mScanCallback is non-null here - it cannot be null when mScanning is true.
+
+ // BluetoothLeScanner will throw an IllegalStateException if stopScan() is called while LE
+ // is not enabled.
+ if (mBtAdapter.isLeEnabled()) {
+ try {
+ mBleScanner.stopScan(mScanCallback);
+ } catch (RuntimeException e) {
+ // Just to be sure not to crash system server here if BluetoothLeScanner throws
+ // another RuntimeException.
+ Slog.w(TAG, "Exception while stopping BLE scanning", e);
+ }
+ }
mScanning = false;
-
- if (mBtAdapter.getState() != STATE_ON && mBtAdapter.getState() != STATE_BLE_ON) {
- Log.d(TAG, "BT Adapter is not turned ON");
- return;
- }
-
- mBleScanner.stopScan(mScanCallback);
}
@MainThread
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index e335a16..09ef03c 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -52,6 +52,7 @@
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -718,7 +719,8 @@
/**
* Returns a package object for the disabled system package name.
*/
- public abstract @Nullable PackageSetting getDisabledSystemPackage(@NonNull String packageName);
+ public abstract @Nullable PackageStateInternal getDisabledSystemPackage(
+ @NonNull String packageName);
/**
* Returns the package name for the disabled system package.
@@ -1334,4 +1336,12 @@
public abstract PackageStateMutator.Result commitPackageStateMutation(
@Nullable PackageStateMutator.InitialState state,
@NonNull Consumer<PackageStateMutator> consumer);
+
+ /**
+ * @return package data snapshot for use with other PackageManager infrastructure. This should
+ * only be used as a parameter passed to another PM related class. Do not call methods on this
+ * directly.
+ */
+ @NonNull
+ public abstract PackageDataSnapshot snapshot();
}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index aae1cc0..c375c73 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -17,6 +17,8 @@
package com.android.server;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
@@ -32,6 +34,9 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FastPrintWriter;
+import com.android.server.pm.Computer;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -51,7 +56,7 @@
final private static boolean localLOGV = DEBUG || false;
final private static boolean localVerificationLOGV = DEBUG || false;
- public void addFilter(F f) {
+ public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
IntentFilter intentFilter = getIntentFilter(f);
if (localLOGV) {
Slog.v(TAG, "Adding filter: " + f);
@@ -420,8 +425,9 @@
return Collections.unmodifiableSet(mFilters);
}
- public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,
- ArrayList<F[]> listCut, int userId) {
+ public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent,
+ String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId,
+ long customFlags) {
ArrayList<R> resultList = new ArrayList<R>();
final boolean debug = localLOGV ||
@@ -431,16 +437,21 @@
final String scheme = intent.getScheme();
int N = listCut.size();
for (int i = 0; i < N; ++i) {
- buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme,
- listCut.get(i), resultList, userId);
+ buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme,
+ listCut.get(i), resultList, userId, customFlags);
}
filterResults(resultList);
sortResults(resultList);
return resultList;
}
- public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
- int userId) {
+ public List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ return queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, 0);
+ }
+
+ protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) {
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
@@ -512,21 +523,22 @@
}
FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
+ Computer computer = (Computer) snapshot;
if (firstTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
- scheme, firstTypeCut, finalList, userId);
+ buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+ scheme, firstTypeCut, finalList, userId, customFlags);
}
if (secondTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
- scheme, secondTypeCut, finalList, userId);
+ buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+ scheme, secondTypeCut, finalList, userId, customFlags);
}
if (thirdTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
- scheme, thirdTypeCut, finalList, userId);
+ buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+ scheme, thirdTypeCut, finalList, userId, customFlags);
}
if (schemeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
- scheme, schemeCut, finalList, userId);
+ buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+ scheme, schemeCut, finalList, userId, customFlags);
}
filterResults(finalList);
sortResults(finalList);
@@ -554,7 +566,7 @@
* "stopped", that is whether it should not be included in the result
* if the intent requests to excluded stopped objects.
*/
- protected boolean isFilterStopped(F filter, int userId) {
+ protected boolean isFilterStopped(PackageStateInternal packageState, @UserIdInt int userId) {
return false;
}
@@ -584,7 +596,8 @@
protected abstract F[] newArray(int size);
@SuppressWarnings("unchecked")
- protected R newResult(F filter, int match, int userId) {
+ protected R newResult(@NonNull Computer computer, F filter, int match, int userId,
+ long customFlags) {
return (R)filter;
}
@@ -764,9 +777,10 @@
return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
}
- private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
- boolean debug, boolean defaultOnly, String resolvedType, String scheme,
- F[] src, List<R> dest, int userId) {
+ private void buildResolveList(@NonNull Computer computer, Intent intent,
+ FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly,
+ String resolvedType, String scheme, F[] src, List<R> dest, int userId,
+ long customFlags) {
final String action = intent.getAction();
final Uri data = intent.getData();
final String packageName = intent.getPackage();
@@ -791,7 +805,8 @@
int match;
if (debug) Slog.v(TAG, "Matching against filter " + filter);
- if (excludingStopped && isFilterStopped(filter, userId)) {
+ if (excludingStopped && isFilterStopped(computer.getPackageStateInternal(packageName),
+ userId)) {
if (debug) {
Slog.v(TAG, " Filter's target is stopped; skipping");
}
@@ -833,7 +848,7 @@
Integer.toHexString(match) + " hasDefault="
+ intentFilter.hasCategory(Intent.CATEGORY_DEFAULT));
if (!defaultOnly || intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)) {
- final R oneResult = newResult(filter, match, userId);
+ final R oneResult = newResult(computer, filter, match, userId, customFlags);
if (debug) Slog.v(TAG, " Created result: " + oneResult);
if (oneResult != null) {
dest.add(oneResult);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6c39a11..6813f3f8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,6 +24,7 @@
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
@@ -395,12 +396,14 @@
import com.android.server.graphics.fonts.FontManagerInternal;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.os.NativeTombstoneManager;
+import com.android.server.pm.Computer;
import com.android.server.pm.Installer;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.uri.GrantUri;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -1105,10 +1108,11 @@
}
@Override
- protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
+ protected BroadcastFilter newResult(@NonNull Computer computer, BroadcastFilter filter,
+ int match, int userId, long customFlags) {
if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
|| userId == filter.owningUserId) {
- return super.newResult(filter, match, userId);
+ return super.newResult(computer, filter, match, userId, customFlags);
}
return null;
}
@@ -13039,7 +13043,7 @@
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
- mReceiverResolver.addFilter(bf);
+ mReceiverResolver.addFilter(getPackageManagerInternal().snapshot(), bf);
}
// Enqueue broadcasts for all existing stickies that match
@@ -13861,6 +13865,7 @@
intent, resolvedType, callingUid, users, broadcastAllowList);
}
if (intent.getComponent() == null) {
+ final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
@@ -13869,7 +13874,7 @@
continue;
}
List<BroadcastFilter> registeredReceiversForUser =
- mReceiverResolver.queryIntent(intent,
+ mReceiverResolver.queryIntent(snapshot, intent,
resolvedType, false /*defaultOnly*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
@@ -13878,7 +13883,7 @@
}
}
} else {
- registeredReceivers = mReceiverResolver.queryIntent(intent,
+ registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
@@ -14339,14 +14344,18 @@
return false;
}
- if (!Build.IS_DEBUGGABLE) {
- int match = mContext.getPackageManager().checkSignatures(
- ii.targetPackage, ii.packageName);
- if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+ int match = mContext.getPackageManager().checkSignatures(
+ ii.targetPackage, ii.packageName);
+ if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+ if (Build.IS_DEBUGGABLE && (flags & INSTR_FLAG_ALWAYS_CHECK_SIGNATURE) == 0) {
+ Slog.w(TAG, "Instrumentation test " + ii.packageName
+ + " doesn't have a signature matching the target " + ii.targetPackage
+ + ", which would not be allowed on the production Android builds");
+ } else {
String msg = "Permission Denial: starting instrumentation "
+ className + " from pid="
+ Binder.getCallingPid()
- + ", uid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ " not allowed because package " + ii.packageName
+ " does not have a signature matching the target "
+ ii.targetPackage;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index bcde343..28b807c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -34,6 +34,7 @@
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_COUNT;
import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -3349,7 +3350,11 @@
pw.println(" --checkin: output checkin format, resetting data.");
pw.println(" --C: output checkin format, not resetting data.");
pw.println(" --proto: output dump in protocol buffer format.");
- pw.println(" --autofill: dump just the autofill-related state of an activity");
+ pw.printf(" %s: dump just the DUMPABLE-related state of an activity. Use the %s "
+ + "option to list the supported DUMPABLEs\n", Activity.DUMP_ARG_DUMP_DUMPABLE,
+ Activity.DUMP_ARG_LIST_DUMPABLES);
+ pw.printf(" %s: show the available dumpables in an activity\n",
+ Activity.DUMP_ARG_LIST_DUMPABLES);
} else {
pw.println("Activity manager (activity) commands:");
pw.println(" help");
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d233c5e..2e80efb 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -255,10 +255,9 @@
public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
SurfaceControl.DynamicDisplayInfo dynamicInfo,
SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
- boolean changed =
- updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode);
- changed |= updateDisplayModesLocked(
- dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs);
+ boolean changed = updateDisplayModesLocked(
+ dynamicInfo.supportedDisplayModes, dynamicInfo.preferredBootDisplayMode,
+ dynamicInfo.activeDisplayModeId, modeSpecs);
changed |= updateStaticInfo(staticInfo);
changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
dynamicInfo.activeColorMode);
@@ -273,10 +272,12 @@
}
public boolean updateDisplayModesLocked(
- SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
- SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+ SurfaceControl.DisplayMode[] displayModes, int preferredSfDisplayModeId,
+ int activeSfDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
- mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId);
+ mActiveSfDisplayMode = getModeById(displayModes, activeSfDisplayModeId);
+ SurfaceControl.DisplayMode preferredSfDisplayMode =
+ getModeById(displayModes, preferredSfDisplayModeId);
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<>();
@@ -335,6 +336,27 @@
}
}
+ boolean preferredModeChanged = false;
+
+ if (preferredSfDisplayModeId != INVALID_MODE_ID && preferredSfDisplayMode != null) {
+ DisplayModeRecord preferredRecord = null;
+ for (DisplayModeRecord record : records) {
+ if (record.hasMatchingMode(preferredSfDisplayMode)) {
+ preferredRecord = record;
+ break;
+ }
+ }
+
+ if (preferredRecord != null) {
+ int preferredModeId = preferredRecord.mMode.getModeId();
+ if (mSurfaceControlProxy.getBootDisplayModeSupport()
+ && mSystemPreferredModeId != preferredModeId) {
+ mSystemPreferredModeId = preferredModeId;
+ preferredModeChanged = true;
+ }
+ }
+ }
+
boolean activeModeChanged = false;
// Check whether SurfaceFlinger or the display device changed the active mode out from
@@ -373,7 +395,7 @@
boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
// If the records haven't changed then we're done here.
if (!recordsChanged) {
- return activeModeChanged;
+ return activeModeChanged || preferredModeChanged;
}
mSupportedModes.clear();
@@ -386,14 +408,14 @@
mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
? mSystemPreferredModeId : activeRecord.mMode.getModeId();
mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
- ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group
+ ? preferredSfDisplayMode.group
: mActiveSfDisplayMode.group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
mDefaultModeId = activeRecord.mMode.getModeId();
mDefaultModeGroup = mActiveSfDisplayMode.group;
- } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
+ } else if (findSfDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
mDefaultModeId = activeRecord.mMode.getModeId();
@@ -545,15 +567,6 @@
return true;
}
- private boolean updateSystemPreferredDisplayMode(int modeId) {
- if (!mSurfaceControlProxy.getBootDisplayModeSupport()
- || mSystemPreferredModeId == modeId) {
- return false;
- }
- mSystemPreferredModeId = modeId;
- return true;
- }
-
private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes,
int modeId) {
for (SurfaceControl.DisplayMode mode : supportedModes) {
@@ -887,8 +900,10 @@
if (mUserPreferredMode == null) {
mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
} else {
+ int preferredSfDisplayModeId = findSfDisplayModeIdLocked(
+ mUserPreferredMode.getModeId(), mDefaultModeGroup);
mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(),
- mUserPreferredMode.getModeId());
+ preferredSfDisplayModeId);
}
}
@@ -923,9 +938,9 @@
// mode based on vendor requirements.
// Note: We prefer the default mode group over the current one as this is the mode
// group the vendor prefers.
- int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+ int baseSfModeId = findSfDisplayModeIdLocked(displayModeSpecs.baseModeId,
mDefaultModeGroup);
- if (baseModeId < 0) {
+ if (baseSfModeId < 0) {
// When a display is hotplugged, it's possible for a mode to be removed that was
// previously valid. Because of the way display changes are propagated through the
// framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -943,7 +958,7 @@
getHandler().sendMessage(PooledLambda.obtainMessage(
LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
getDisplayTokenLocked(),
- new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
+ new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId,
mDisplayModeSpecs.allowGroupSwitching,
mDisplayModeSpecs.primaryRefreshRateRange.min,
mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -1090,14 +1105,14 @@
pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
- private int findDisplayModeIdLocked(int modeId, int modeGroup) {
- int matchingModeId = INVALID_MODE_ID;
- DisplayModeRecord record = mSupportedModes.get(modeId);
+ private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
+ int matchingSfDisplayModeId = INVALID_MODE_ID;
+ DisplayModeRecord record = mSupportedModes.get(displayModeId);
if (record != null) {
for (SurfaceControl.DisplayMode mode : mSfDisplayModes) {
if (record.hasMatchingMode(mode)) {
- if (matchingModeId == INVALID_MODE_ID) {
- matchingModeId = mode.id;
+ if (matchingSfDisplayModeId == INVALID_MODE_ID) {
+ matchingSfDisplayModeId = mode.id;
}
// Prefer to return a mode that matches the modeGroup
@@ -1107,7 +1122,7 @@
}
}
}
- return matchingModeId;
+ return matchingSfDisplayModeId;
}
// Returns a mode with id = modeId.
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 1139d28..bb8a744 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -24,6 +24,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.os.Environment;
import android.os.FileObserver;
import android.os.Handler;
@@ -38,6 +39,8 @@
import com.android.internal.util.XmlUtils;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
+import com.android.server.LocalServices;
+import com.android.server.pm.Computer;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -75,6 +78,9 @@
private final RuleObserver mObserver;
+ @NonNull
+ private PackageManagerInternal mPackageManager;
+
private FirewallIntentResolver mActivityResolver = new FirewallIntentResolver();
private FirewallIntentResolver mBroadcastResolver = new FirewallIntentResolver();
private FirewallIntentResolver mServiceResolver = new FirewallIntentResolver();
@@ -123,6 +129,13 @@
mObserver.startWatching();
}
+ private PackageManagerInternal getPackageManager() {
+ if (mPackageManager == null) {
+ mPackageManager = LocalServices.getService(PackageManagerInternal.class);
+ }
+ return mPackageManager;
+ }
+
/**
* This is called from ActivityManager to check if a start activity intent should be allowed.
* It is assumed the caller is already holding the global ActivityManagerService lock.
@@ -154,7 +167,8 @@
// For the first pass, find all the rules that have at least one intent-filter or
// component-filter that matches this intent
List<Rule> candidateRules;
- candidateRules = resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, 0);
+ candidateRules = resolver.queryIntent(getPackageManager().snapshot(), intent, resolvedType,
+ false /*defaultOnly*/, 0);
if (candidateRules == null) {
candidateRules = new ArrayList<Rule>();
}
@@ -375,7 +389,7 @@
for (int ruleIndex=0; ruleIndex<rules.size(); ruleIndex++) {
Rule rule = rules.get(ruleIndex);
for (int i=0; i<rule.getIntentFilterCount(); i++) {
- resolver.addFilter(rule.getIntentFilter(i));
+ resolver.addFilter(null, rule.getIntentFilter(i));
}
for (int i=0; i<rule.getComponentFilterCount(); i++) {
resolver.addComponentFilter(rule.getComponentFilter(i), rule);
@@ -512,7 +526,8 @@
}
@Override
- protected Rule newResult(FirewallIntentFilter filter, int match, int userId) {
+ protected Rule newResult(@NonNull Computer computer, FirewallIntentFilter filter,
+ int match, int userId, long customFlags) {
return filter.rule;
}
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 0d9ccd2..2a4882a 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -51,6 +51,8 @@
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
@@ -98,7 +100,7 @@
* {@link ComputerEngine} and {@link ComputerLocked}.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-public interface Computer {
+public interface Computer extends PackageDataSnapshot {
/**
* Every method must be annotated.
@@ -178,6 +180,18 @@
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId);
+
+ /**
+ * Filters out ephemeral activities.
+ * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
+ * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
+ *
+ * @param resolveInfos The pre-filtered list of resolved activities
+ * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
+ * is performed.
+ * @param intent
+ * @return A filtered list of resolved activities.
+ */
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
@@ -648,4 +662,16 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId);
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ @NonNull
+ ComponentResolverApi getComponentResolver();
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ @Nullable
+ PackageStateInternal getDisabledSystemPackage(@NonNull String packageName);
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ @Nullable
+ ResolveInfo getInstantAppInstallerInfo();
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index c437697..9e87898 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -49,7 +49,6 @@
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
@@ -59,6 +58,7 @@
import static com.android.server.pm.PackageManagerService.HIDE_EPHEMERAL_APIS;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import android.Manifest;
import android.annotation.NonNull;
@@ -143,6 +143,7 @@
import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationUtils;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -362,7 +363,7 @@
private final PermissionManagerServiceInternal mPermissionManager;
private final ApexManager mApexManager;
private final PackageManagerServiceInjector mInjector;
- private final ComponentResolver mComponentResolver;
+ private final ComponentResolverApi mComponentResolver;
private final InstantAppResolverConnection mInstantAppResolverConnection;
private final DefaultAppProvider mDefaultAppProvider;
private final DomainVerificationManagerInternal mDomainVerificationManager;
@@ -644,7 +645,7 @@
// reader
String pkgName = intent.getPackage();
if (pkgName == null) {
- final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
resolvedType, flags, userId);
if (resolveInfos == null) {
return Collections.emptyList();
@@ -654,7 +655,7 @@
}
final AndroidPackage pkg = mPackages.get(pkgName);
if (pkg != null) {
- final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
resolvedType, flags, pkg.getServices(),
userId);
if (resolveInfos == null) {
@@ -691,7 +692,7 @@
}
// Check for results in the current profile.
- result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+ result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
intent, resolvedType, flags, userId), userId);
addInstant = isInstantAppResolutionAllowed(intent, result, userId,
false /*skipPackageCheck*/, flags);
@@ -753,7 +754,7 @@
result = null;
if (setting != null && setting.getAndroidPackage() != null && (resolveForStart
|| !shouldFilterApplication(setting, filterCallingUid, userId))) {
- result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+ result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
userId), userId);
}
@@ -1181,7 +1182,7 @@
sourceUserId)) {
return null;
}
- List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+ List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
resolvedType, flags, parentUserId);
if (resultTargetUser == null || resultTargetUser.isEmpty()) {
@@ -1232,7 +1233,7 @@
Intent intent, String resolvedType, int userId) {
CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
if (resolver != null) {
- return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
+ return resolver.queryIntent(this, intent, resolvedType, false /*defaultOnly*/, userId);
}
return null;
}
@@ -1447,7 +1448,7 @@
ResolveInfo localInstantApp = null;
boolean blockResolution = false;
if (!alreadyResolvedLocally) {
- final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(
+ final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(this,
intent,
resolvedType,
flags
@@ -1493,8 +1494,8 @@
null /*callingFeatureId*/, isRequesterInstantApp, userId,
null /*verificationBundle*/, resolveForStart,
digest.getDigestPrefixSecure(), token);
- auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
- mInstantAppResolverConnection, requestObject);
+ auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(this,
+ mUserManager, mInstantAppResolverConnection, requestObject);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
} else {
// we have an instant application locally, but, we can't admit that since
@@ -1830,7 +1831,7 @@
return null;
}
- List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+ List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
resolvedType, flags, targetUserId);
if (CollectionUtils.isEmpty(resultTargetUser)) {
return null;
@@ -2586,7 +2587,7 @@
mSettings.getPersistentPreferredActivities(userId);
//TODO(b/158003772): Remove double query
List<PersistentPreferredActivity> pprefs = ppir != null
- ? ppir.queryIntent(intent, resolvedType,
+ ? ppir.queryIntent(this, intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: new ArrayList<>();
@@ -3244,7 +3245,7 @@
// Get the list of preferred activities that handle the intent
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
List<PreferredActivity> prefs = pir != null
- ? pir.queryIntent(intent, resolvedType,
+ ? pir.queryIntent(this, intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: null;
@@ -3376,7 +3377,7 @@
pa.mPref.mComponent,
pa.mPref.mAlways);
pir.removeFilter(pa);
- pir.addFilter(freshPa);
+ pir.addFilter(this, freshPa);
result.mChanged = true;
} else {
if (DEBUG_PREFERRED) {
@@ -3399,7 +3400,7 @@
PreferredActivity lastChosen = new PreferredActivity(
pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
false);
- pir.addFilter(lastChosen);
+ pir.addFilter(this, lastChosen);
result.mChanged = true;
}
result.mPreferredResolveInfo = null;
@@ -3454,7 +3455,7 @@
Slog.v(TAG, "Looking for persistent preferred activities...");
}
List<PersistentPreferredActivity> pprefs = ppir != null
- ? ppir.queryIntent(intent, resolvedType,
+ ? ppir.queryIntent(this, intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: null;
@@ -4661,7 +4662,8 @@
int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
- final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
+ final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
+ userId);
boolean checkedGrants = false;
if (providerInfo != null) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
@@ -4739,7 +4741,7 @@
final List<String> names = new ArrayList<>();
final List<ProviderInfo> infos = new ArrayList<>();
final int callingUserId = UserHandle.getCallingUserId();
- mComponentResolver.querySyncProviders(names, infos, safeMode, callingUserId);
+ mComponentResolver.querySyncProviders(this, names, infos, safeMode, callingUserId);
for (int i = infos.size() - 1; i >= 0; i--) {
final ProviderInfo providerInfo = infos.get(i);
final PackageStateInternal ps = mSettings.getPackage(providerInfo.packageName);
@@ -4771,8 +4773,8 @@
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId);
ArrayList<ProviderInfo> finalList = null;
- final List<ProviderInfo> matchList =
- mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
+ final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName,
+ metaDataKey, uid, flags, userId);
final int listSize = (matchList == null ? 0 : matchList.size());
for (int i = 0; i < listSize; i++) {
final ProviderInfo providerInfo = matchList.get(i);
@@ -5674,4 +5676,22 @@
public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
return mSettings.getSharedUserPackages(sharedUserAppId);
}
+
+ @NonNull
+ @Override
+ public ComponentResolverApi getComponentResolver() {
+ return mComponentResolver;
+ }
+
+ @Nullable
+ @Override
+ public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+ return mSettings.getDisabledSystemPkg(packageName);
+ }
+
+ @Nullable
+ @Override
+ public ResolveInfo getInstantAppInstallerInfo() {
+ return mInstantAppInstallerInfo;
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 583348b..5d89c7d 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -48,6 +48,7 @@
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
@@ -868,4 +869,28 @@
return super.getSharedUserPackages(sharedUserAppId);
}
}
+
+ @NonNull
+ @Override
+ public ComponentResolverApi getComponentResolver() {
+ synchronized (mLock) {
+ return super.getComponentResolver();
+ }
+ }
+
+ @Nullable
+ @Override
+ public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+ synchronized (mLock) {
+ return super.getDisabledSystemPackage(packageName);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ResolveInfo getInstantAppInstallerInfo() {
+ synchronized (mLock) {
+ return super.getInstantAppInstallerInfo();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 72e67da..216ad71 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -49,6 +49,7 @@
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
@@ -1299,4 +1300,28 @@
return current.mComputer.getSharedUserPackages(sharedUserAppId);
}
}
+
+ @NonNull
+ @Override
+ public ComponentResolverApi getComponentResolver() {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getComponentResolver();
+ }
+ }
+
+ @Nullable
+ @Override
+ public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getDisabledSystemPackage(packageName);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ResolveInfo getInstantAppInstallerInfo() {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getInstantAppInstallerInfo();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index d5a882b..2b46ed4 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -405,7 +405,8 @@
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
- mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
+ mPm.mComponentResolver.dumpContentProviders(mPm.snapshotComputer(), pw, dumpState,
+ packageName);
}
if (!checkin
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 2ef092e..234a230 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -36,6 +36,7 @@
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.PackageManagerInternal.PACKAGE_SETUP_WIZARD;
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
@@ -454,7 +455,8 @@
KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
ksms.addScannedPackageLPw(pkg);
- mPm.mComponentResolver.addAllComponents(pkg, chatty);
+ mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage,
+ mPm.snapshotComputer());
mPm.mAppsFilter.addPackage(pkgSetting, isReplace);
mPm.addAllPackageProperties(pkg);
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 79f8dc1..92d6a82 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -57,6 +57,7 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.server.pm.InstantAppResolverConnection.ConnectionException;
import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback;
+import com.android.server.pm.resolution.ComponentResolver;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -134,8 +135,9 @@
}
}
- public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
- InstantAppResolverConnection connection, InstantAppRequest requestObj) {
+ public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(@NonNull Computer computer,
+ @NonNull UserManagerService userManager, InstantAppResolverConnection connection,
+ InstantAppRequest requestObj) {
final long startTime = System.currentTimeMillis();
final String token = requestObj.token;
if (DEBUG_INSTANT) {
@@ -149,7 +151,7 @@
final List<InstantAppResolveInfo> instantAppResolveInfoList =
connection.getInstantAppResolveInfoList(buildRequestInfo(requestObj));
if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
- resolveInfo = InstantAppResolver.filterInstantAppIntent(
+ resolveInfo = InstantAppResolver.filterInstantAppIntent(computer, userManager,
instantAppResolveInfoList, origIntent, requestObj.resolvedType,
requestObj.userId, origIntent.getPackage(), token,
requestObj.hostDigestPrefixSecure);
@@ -187,9 +189,10 @@
return resolveInfo;
}
- public static void doInstantAppResolutionPhaseTwo(Context context,
- InstantAppResolverConnection connection, InstantAppRequest requestObj,
- ActivityInfo instantAppInstaller, Handler callbackHandler) {
+ public static void doInstantAppResolutionPhaseTwo(Context context, @NonNull Computer computer,
+ @NonNull UserManagerService userManager, InstantAppResolverConnection connection,
+ InstantAppRequest requestObj, ActivityInfo instantAppInstaller,
+ Handler callbackHandler) {
final long startTime = System.currentTimeMillis();
final String token = requestObj.token;
if (DEBUG_INSTANT) {
@@ -205,7 +208,7 @@
final Intent failureIntent;
if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
final AuxiliaryResolveInfo instantAppIntentInfo =
- InstantAppResolver.filterInstantAppIntent(
+ InstantAppResolver.filterInstantAppIntent(computer, userManager,
instantAppResolveInfoList, origIntent, null /*resolvedType*/,
0 /*userId*/, origIntent.getPackage(),
token, requestObj.hostDigestPrefixSecure);
@@ -386,7 +389,8 @@
);
}
- private static AuxiliaryResolveInfo filterInstantAppIntent(
+ private static AuxiliaryResolveInfo filterInstantAppIntent(@NonNull Computer computer,
+ @NonNull UserManagerService userManager,
List<InstantAppResolveInfo> instantAppResolveInfoList, Intent origIntent,
String resolvedType, int userId, String packageName, String token,
int[] hostDigestPrefixSecure) {
@@ -421,7 +425,8 @@
}
// We matched a resolve info; resolve the filters to see if anything matches completely.
List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters(
- origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo);
+ computer, userManager, origIntent, resolvedType, userId, packageName, token,
+ instantAppResolveInfo);
if (matchFilters != null) {
if (matchFilters.isEmpty()) {
requiresSecondPhase = true;
@@ -464,7 +469,8 @@
*
*/
private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters(
- Intent origIntent, String resolvedType, int userId, String packageName, String token,
+ @NonNull Computer computer, @NonNull UserManagerService userManager, Intent origIntent,
+ String resolvedType, int userId, String packageName, String token,
InstantAppResolveInfo instantAppInfo) {
if (instantAppInfo.shouldLetInstallerDecide()) {
return Collections.singletonList(
@@ -490,7 +496,7 @@
return Collections.emptyList();
}
final ComponentResolver.InstantAppIntentResolver instantAppResolver =
- new ComponentResolver.InstantAppIntentResolver();
+ new ComponentResolver.InstantAppIntentResolver(userManager);
for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
@@ -508,7 +514,7 @@
&& filter.hasCategory(Intent.CATEGORY_BROWSABLE)) {
continue;
}
- instantAppResolver.addFilter(
+ instantAppResolver.addFilter(computer,
new AuxiliaryResolveInfo.AuxiliaryFilter(
filter,
instantAppInfo,
@@ -518,8 +524,8 @@
}
}
List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList =
- instantAppResolver.queryIntent(
- origIntent, resolvedType, false /*defaultOnly*/, userId);
+ instantAppResolver.queryIntent(computer, origIntent, resolvedType,
+ false /*defaultOnly*/, userId);
if (!matchedResolveInfoList.isEmpty()) {
if (DEBUG_INSTANT) {
Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList);
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index ec71940..46e2aa3 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -313,6 +313,8 @@
}
case INSTANT_APP_RESOLUTION_PHASE_TWO: {
InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
+ mPm.snapshotComputer(),
+ mPm.mUserManager,
mPm.mInstantAppResolverConnection,
(InstantAppRequest) msg.obj,
mPm.mInstantAppInstallerActivity,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 725e92a..0b7a6a7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,6 @@
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManager.PropertyLocation;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackagePartitions;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
@@ -242,6 +241,8 @@
import com.android.server.pm.pkg.mutate.PackageStateWrite;
import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.resolution.ComponentResolver;
+import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -791,9 +792,6 @@
private final AtomicInteger mNextMoveId = new AtomicInteger();
final MovePackageHelper.MoveCallbacks mMoveCallbacks;
- // Cache of users who need badging.
- private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
-
/**
* Token for keys in mPendingVerification.
* Handler thread only!
@@ -912,6 +910,8 @@
final UserManagerService mUserManager;
+ final UserNeedsBadgingCache mUserNeedsBadging;
+
// Stores a list of users whose package restrictions file needs to be updated
final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
@@ -992,7 +992,7 @@
public final ApplicationInfo androidApplication;
public final String appPredictionServicePackage;
public final AppsFilter appsFilter;
- public final ComponentResolver componentResolver;
+ public final ComponentResolverApi componentResolver;
public final PackageManagerService service;
public final WatchedArrayMap<String, Integer> frozenPackages;
public final SharedLibrariesRead sharedLibraries;
@@ -1081,17 +1081,10 @@
private final SnapshotStatistics mSnapshotStatistics;
/**
- * Return the live computer.
- */
- Computer liveComputer() {
- return mLiveComputer;
- }
-
- /**
* Return the cached computer. The method will rebuild the cached computer if necessary.
* The live computer will be returned if snapshots are disabled.
*/
- Computer snapshotComputer() {
+ public Computer snapshotComputer() {
if (Thread.holdsLock(mLock)) {
// If the current thread holds mLock then it may have modified state but not
// yet invalidated the snapshot. Always give the thread the live computer.
@@ -1171,8 +1164,8 @@
@Override
public void notifyPackagesReplacedReceived(String[] packages) {
- ArraySet<String> packagesToNotify =
- mComputer.getNotifyPackagesForReplacedReceived(packages);
+ Computer computer = snapshotComputer();
+ ArraySet<String> packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages);
for (int index = 0; index < packagesToNotify.size(); index++) {
notifyInstallObserver(packagesToNotify.valueAt(index));
}
@@ -1441,7 +1434,7 @@
context, lock, installer, installLock, new PackageAbiHelperImpl(),
backgroundHandler,
SYSTEM_PARTITIONS,
- (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
+ (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),
(i, pm) -> PermissionManagerService.create(context,
i.getSystemConfig().getAvailableFeatures()),
(i, pm) -> new UserManagerService(context, pm,
@@ -1619,6 +1612,7 @@
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
+ mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
mHandler = injector.getHandler();
mSharedLibraries = injector.getSharedLibrariesImpl();
@@ -1744,6 +1738,7 @@
mTestUtilityService = LocalServices.getService(TestUtilityService.class);
LocalServices.addService(PackageManagerInternal.class, mPmInternal);
mUserManager = injector.getUserManagerService();
+ mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
mComponentResolver = injector.getComponentResolver();
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
@@ -1846,7 +1841,9 @@
mAppDataHelper);
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
mPreferredActivityHelper = new PreferredActivityHelper(this);
- mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
+ mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
+ injector.getCompatibility(), mUserManager, mDomainVerificationManager,
+ mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity);
mDexOptHelper = new DexOptHelper(this);
mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
mProtectedPackages);
@@ -1862,6 +1859,7 @@
registerObservers(true);
}
+ Computer computer = mLiveComputer;
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
@@ -1975,12 +1973,12 @@
userIds, startTime);
// Resolve the storage manager.
- mStorageManagerPackage = getStorageManagerPackageName();
+ mStorageManagerPackage = getStorageManagerPackageName(computer);
// Resolve protected action filters. Only the setup wizard is allowed to
// have a high priority filter for these actions.
- mSetupWizardPackage = getSetupWizardPackageNameImpl();
- mComponentResolver.fixProtectedFilterPriorities();
+ mSetupWizardPackage = getSetupWizardPackageNameImpl(computer);
+ mComponentResolver.fixProtectedFilterPriorities(mPmInternal.getSetupWizardPackageName());
mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
@@ -2108,13 +2106,13 @@
SystemClock.uptimeMillis());
if (!mOnlyCore) {
- mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
- mRequiredInstallerPackage = getRequiredInstallerLPr();
- mRequiredUninstallerPackage = getRequiredUninstallerLPr();
+ mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(computer);
+ mRequiredInstallerPackage = getRequiredInstallerLPr(computer);
+ mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);
ComponentName intentFilterVerifierComponent =
- getIntentFilterVerifierComponentNameLPr();
+ getIntentFilterVerifierComponentNameLPr(computer);
ComponentName domainVerificationAgent =
- getDomainVerificationAgentComponentNameLPr();
+ getDomainVerificationAgentComponentNameLPr(computer);
DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy(
intentFilterVerifierComponent, domainVerificationAgent, mContext,
@@ -2137,7 +2135,7 @@
// PermissionController hosts default permission granting and role management, so it's a
// critical part of the core system.
- mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
+ mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer);
mSettings.setPermissionControllerVersion(
getPackageInfo(mRequiredPermissionControllerPackage, 0,
@@ -2170,7 +2168,8 @@
mInstantAppResolverConnection =
mInjector.getInstantAppResolverConnection(instantAppResolverComponent);
mInstantAppResolverSettingsComponent =
- getInstantAppResolverSettingsLPr(instantAppResolverComponent);
+ getInstantAppResolverSettingsLPr(computer,
+ instantAppResolverComponent);
} else {
mInstantAppResolverConnection = null;
mInstantAppResolverSettingsComponent = null;
@@ -2258,11 +2257,13 @@
"persist.pm.mock-upgrade", false /* default */);
}
- private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
+ @Nullable
+ private String getRequiredButNotReallyRequiredVerifierLPr(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
final List<ResolveInfo> matches =
- mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+ mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
+ PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, Binder.getCallingUid());
if (matches.size() == 1) {
@@ -2300,12 +2301,13 @@
return servicesExtensionPackage;
}
- private @NonNull String getRequiredInstallerLPr() {
+ private @NonNull String getRequiredInstallerLPr(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE);
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+ final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent,
+ PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM);
if (matches.size() == 1) {
@@ -2319,14 +2321,14 @@
}
}
- private @NonNull String getRequiredUninstallerLPr() {
+ private @NonNull String getRequiredUninstallerLPr(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null));
- final ResolveInfo resolveInfo = resolveIntent(intent, null,
- MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM);
+ final ResolveInfo resolveInfo = mResolveIntentHelper.resolveIntentInternal(computer, intent,
+ null, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ 0 /*privateResolveFlags*/, UserHandle.USER_SYSTEM, false, Binder.getCallingUid());
if (resolveInfo == null ||
mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) {
throw new RuntimeException("There must be exactly one uninstaller; found "
@@ -2335,11 +2337,11 @@
return resolveInfo.getComponentInfo().packageName;
}
- private @NonNull String getRequiredPermissionControllerLPr() {
+ private @NonNull String getRequiredPermissionControllerLPr(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM);
if (matches.size() == 1) {
@@ -2354,11 +2356,13 @@
}
}
- private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
+ @NonNull
+ private ComponentName getIntentFilterVerifierComponentNameLPr(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
final List<ResolveInfo> matches =
- mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+ mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
+ PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, Binder.getCallingUid());
ResolveInfo best = null;
@@ -2384,10 +2388,10 @@
}
@Nullable
- private ComponentName getDomainVerificationAgentComponentNameLPr() {
+ private ComponentName getDomainVerificationAgentComponentNameLPr(@NonNull Computer computer) {
Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
List<ResolveInfo> matches =
- mResolveIntentHelper.queryIntentReceiversInternal(intent, null,
+ mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, Binder.getCallingUid());
ResolveInfo best = null;
@@ -2496,13 +2500,14 @@
| MATCH_DIRECT_BOOT_UNAWARE
| Intent.FLAG_IGNORE_EPHEMERAL
| (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY);
+ final Computer computer = snapshotComputer();
final Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
List<ResolveInfo> matches = null;
for (String action : orderedActions) {
intent.setAction(action);
- matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+ matches = computer.queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
resolveFlags, UserHandle.USER_SYSTEM);
if (matches.isEmpty()) {
if (DEBUG_INSTANT) {
@@ -2532,14 +2537,14 @@
}
}
- private @Nullable ComponentName getInstantAppResolverSettingsLPr(
+ private @Nullable ComponentName getInstantAppResolverSettingsLPr(@NonNull Computer computer,
@NonNull ComponentName resolver) {
final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
.addCategory(Intent.CATEGORY_DEFAULT)
.setPackage(resolver.getPackageName());
final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
- List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
- UserHandle.USER_SYSTEM);
+ List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
+ resolveFlags, UserHandle.USER_SYSTEM);
if (matches.isEmpty()) {
return null;
}
@@ -2942,27 +2947,6 @@
}
/**
- * Update given flags when being used to request {@link PackageInfo}.
- */
- private long updateFlagsForPackage(long flags, int userId) {
- return mComputer.updateFlagsForPackage(flags, userId);
- }
-
- /**
- * Update given flags when being used to request {@link ApplicationInfo}.
- */
- private long updateFlagsForApplication(long flags, int userId) {
- return mComputer.updateFlagsForApplication(flags, userId);
- }
-
- /**
- * Update given flags when being used to request {@link ComponentInfo}.
- */
- private long updateFlagsForComponent(long flags, int userId) {
- return mComputer.updateFlagsForComponent(flags, userId);
- }
-
- /**
* Update given flags when being used to request {@link ResolveInfo}.
* <p>Instant apps are resolved specially, depending upon context. Minimally,
* {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
@@ -3314,8 +3298,8 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
@PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- return mResolveIntentHelper.resolveIntentInternal(intent, resolvedType, flags,
- 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
+ return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(), intent, resolvedType,
+ flags, 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
}
@Override
@@ -3414,8 +3398,8 @@
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
- return new ParceledListSlice<>(
- queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+ return new ParceledListSlice<>(snapshotComputer().queryIntentActivitiesInternal(intent,
+ resolvedType, flags, userId));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -3429,68 +3413,28 @@
return mComputer.getInstantAppPackageName(callingUid);
}
- @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- return mComputer.queryIntentActivitiesInternal(intent,
- resolvedType, flags, userId);
- }
-
- @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
- @PrivateResolveFlags long privateResolveFlags, int filterCallingUid, int userId,
- boolean resolveForStart, boolean allowDynamicSplits) {
- return mComputer.queryIntentActivitiesInternal(intent,
- resolvedType, flags, privateResolveFlags,
- filterCallingUid, userId, resolveForStart, allowDynamicSplits);
- }
-
- private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
- int parentUserId) {
- return mComputer.getCrossProfileDomainPreferredLpr(intent,
- resolvedType, flags, sourceUserId, parentUserId);
- }
-
- /**
- * Filters out ephemeral activities.
- * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
- * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
- *
- * @param resolveInfos The pre-filtered list of resolved activities
- * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
- * is performed.
- * @param intent
- * @return A filtered list of resolved activities.
- */
- List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
- String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
- boolean resolveForStart, int userId, Intent intent) {
- return mComputer.applyPostResolutionFilter(resolveInfos,
- ephemeralPkgName, allowDynamicSplits, filterCallingUid,
- resolveForStart, userId, intent);
- }
-
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
- caller, specifics, specificTypes, intent, resolvedType, flags, userId));
+ snapshotComputer(), caller, specifics, specificTypes, intent, resolvedType, flags,
+ userId));
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(intent,
- resolvedType, flags, userId, Binder.getCallingUid()));
+ return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(
+ snapshotComputer(), intent, resolvedType, flags, userId, Binder.getCallingUid()));
}
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType,
@PackageManager.ResolveInfoFlagsBits long flags, int userId) {
final int callingUid = Binder.getCallingUid();
- return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
- callingUid);
+ return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent, resolvedType,
+ flags, userId, callingUid);
}
@Override
@@ -3513,7 +3457,7 @@
public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
- intent, resolvedType, flags, userId));
+ snapshotComputer(), intent, resolvedType, flags, userId));
}
@Override
@@ -3907,15 +3851,9 @@
synchronized (mLock) {
mPackageUsage.writeNow(mSettings.getPackagesLocked());
- // This is the last chance to write out pending restriction settings
- if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
- mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- synchronized (mDirtyUsers) {
- for (int userId : mDirtyUsers) {
- mSettings.writePackageRestrictionsLPr(userId);
- }
- mDirtyUsers.clear();
- }
+ if (mHandler.hasMessages(WRITE_SETTINGS)) {
+ mHandler.removeMessages(WRITE_SETTINGS);
+ writeSettings();
}
}
}
@@ -5409,7 +5347,7 @@
}
}
}
- resolver.addFilter(newFilter);
+ resolver.addFilter(snapshotComputer(), newFilter);
}
scheduleWritePackageRestrictions(sourceUserId);
}
@@ -5498,11 +5436,11 @@
mPreferredActivityHelper.setHomeActivity(comp, userId);
}
- private @Nullable String getSetupWizardPackageNameImpl() {
+ private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
| MATCH_DISABLED_COMPONENTS,
UserHandle.myUserId());
@@ -5515,10 +5453,10 @@
}
}
- private @Nullable String getStorageManagerPackageName() {
+ private @Nullable String getStorageManagerPackageName(@NonNull Computer computer) {
final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
| MATCH_DISABLED_COMPONENTS,
UserHandle.myUserId());
@@ -6784,21 +6722,7 @@
}
boolean userNeedsBadging(int userId) {
- int index = mUserNeedsBadging.indexOfKey(userId);
- if (index < 0) {
- final UserInfo userInfo;
- final long token = Binder.clearCallingIdentity();
- try {
- userInfo = mUserManager.getUserInfo(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- final boolean b;
- b = userInfo != null && userInfo.isManagedProfile();
- mUserNeedsBadging.put(userId, b);
- return b;
- }
- return mUserNeedsBadging.valueAt(index);
+ return mUserNeedsBadging.get(userId);
}
@Nullable
@@ -7034,16 +6958,14 @@
}
@Override
- public PackageSetting getDisabledSystemPackage(@NonNull String packageName) {
- synchronized (mLock) {
- return mSettings.getDisabledSystemPkgLPr(packageName);
- }
+ public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+ return snapshotComputer().getDisabledSystemPackage(packageName);
}
@Override
public @Nullable
String getDisabledSystemPackageName(@NonNull String packageName) {
- PackageSetting disabledPkgSetting = getDisabledSystemPackage(
+ PackageStateInternal disabledPkgSetting = getDisabledSystemPackage(
packageName);
AndroidPackage disabledPkg = disabledPkgSetting == null
? null : disabledPkgSetting.getPkg();
@@ -7183,9 +7105,8 @@
public List<ResolveInfo> queryIntentActivities(
Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId) {
- return PackageManagerService.this
- .queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
- userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
+ return snapshotComputer().queryIntentActivitiesInternal(intent, resolvedType, flags,
+ userId);
}
@Override
@@ -7193,7 +7114,7 @@
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId) {
return PackageManagerService.this.mResolveIntentHelper.queryIntentReceiversInternal(
- intent, resolvedType, flags, userId, filterCallingUid);
+ snapshotComputer(), intent, resolvedType, flags, userId, filterCallingUid);
}
@Override
@@ -7436,7 +7357,7 @@
@PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid) {
- return mResolveIntentHelper.resolveIntentInternal(
+ return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(),
intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
filterCallingUid);
}
@@ -7444,8 +7365,8 @@
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType,
@PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
- return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
- callingUid);
+ return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent,
+ resolvedType, flags, userId, callingUid);
}
@Override
@@ -7900,6 +7821,12 @@
@NonNull Consumer<PackageStateMutator> consumer) {
return PackageManagerService.this.commitPackageStateMutation(state, consumer);
}
+
+ @NonNull
+ @Override
+ public Computer snapshot() {
+ return snapshotComputer();
+ }
}
private boolean setEnabledOverlayPackages(@UserIdInt int userId,
@@ -8346,16 +8273,6 @@
}
}
- private void applyMimeGroupChanges(String packageName, String mimeGroup) {
- if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) {
- Binder.withCleanCallingIdentity(() ->
- mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
- UserHandle.USER_ALL));
- }
-
- mPmInternal.writeSettings(false);
- }
-
@Override
public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
enforceOwnerRights(packageName, Binder.getCallingUid());
@@ -8375,7 +8292,13 @@
commitPackageStateMutation(null, packageName, packageStateWrite -> {
packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
});
- applyMimeGroupChanges(packageName, mimeGroup);
+ if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
+ Binder.withCleanCallingIdentity(() ->
+ mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
+ UserHandle.USER_ALL));
+ }
+
+ scheduleWriteSettings();
}
@Override
@@ -8607,8 +8530,8 @@
@Override
public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
String featureId, int userId) throws RemoteException {
- return mResolveIntentHelper.getLaunchIntentSenderForPackage(packageName, callingPackage,
- featureId, userId);
+ return mResolveIntentHelper.getLaunchIntentSenderForPackage(snapshotComputer(),
+ packageName, callingPackage, featureId, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 05bb01e..a02237f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -34,6 +34,7 @@
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.resolution.ComponentResolver;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 2f56bfe..a9471cf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -93,6 +93,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import dalvik.system.VMRuntime;
@@ -1064,7 +1065,7 @@
// Static to give access to ComputeEngine
public static void applyEnforceIntentFilterMatching(
- PlatformCompat compat, ComponentResolver resolver,
+ PlatformCompat compat, ComponentResolverApi resolver,
List<ResolveInfo> resolveInfos, boolean isReceiver,
Intent intent, String resolvedType, int filterCallingUid) {
// Do not enforce filter matching when the caller is system or root.
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index bb82e6a..8c49baf 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -140,8 +140,8 @@
return false;
}
final Intent intent = mPm.getHomeIntent();
- final List<ResolveInfo> resolveInfos = mPm.queryIntentActivitiesInternal(intent, null,
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
+ final List<ResolveInfo> resolveInfos = mPm.snapshotComputer().queryIntentActivitiesInternal(
+ intent, null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
intent, null, 0, resolveInfos, true, false, false, userId);
final String packageName = preferredResolveInfo != null
@@ -209,7 +209,8 @@
if (removeExisting && existing != null) {
Settings.removeFilters(pir, filter, existing);
}
- pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
+ pir.addFilter(mPm.snapshotComputer(),
+ new PreferredActivity(filter, match, set, activity, always));
mPm.scheduleWritePackageRestrictions(userId);
}
if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(userId))) {
@@ -394,6 +395,7 @@
}
synchronized (mPm.mLock) {
mPm.mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
+ mPm.snapshotComputer(),
new PersistentPreferredActivity(filter, activity, true));
mPm.scheduleWritePackageRestrictions(userId);
}
@@ -672,8 +674,8 @@
0, userId, callingUid, false /*includeInstantApps*/,
mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
0));
- final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
- flags, userId);
+ final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+ resolvedType, flags, userId);
synchronized (mPm.mLock) {
return mPm.findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
userId);
@@ -699,8 +701,8 @@
filter.dump(new PrintStreamPrinter(System.out), " ");
}
intent.setComponent(null);
- final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
- flags, userId);
+ final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+ resolvedType, flags, userId);
// Find any earlier preferred or last chosen entries and nuke them
findPreferredActivityNotLocked(
intent, resolvedType, flags, query, false, true, false, userId);
@@ -715,8 +717,8 @@
}
final int userId = UserHandle.getCallingUserId();
if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
- final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
- flags, userId);
+ final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+ resolvedType, flags, userId);
return findPreferredActivityNotLocked(
intent, resolvedType, flags, query, false, false, false, userId);
}
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 2aff90f..25356a4 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -27,6 +27,8 @@
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
@@ -50,23 +52,52 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.ArrayUtils;
+import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
+import java.util.function.Supplier;
final class ResolveIntentHelper {
- private final PackageManagerService mPm;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final PlatformCompat mPlatformCompat;
+ @NonNull
+ private final UserManagerService mUserManager;
+ @NonNull
private final PreferredActivityHelper mPreferredActivityHelper;
+ @NonNull
+ private final DomainVerificationManagerInternal mDomainVerificationManager;
+ @NonNull
+ private final UserNeedsBadgingCache mUserNeedsBadging;
+ @NonNull
+ private final Supplier<ResolveInfo> mResolveInfoSupplier;
+ @NonNull
+ private final Supplier<ActivityInfo> mInstantAppInstallerActivitySupplier;
- // TODO(b/198166813): remove PMS dependency
- ResolveIntentHelper(PackageManagerService pm, PreferredActivityHelper preferredActivityHelper) {
- mPm = pm;
+ ResolveIntentHelper(@NonNull Context context,
+ @NonNull PreferredActivityHelper preferredActivityHelper,
+ @NonNull PlatformCompat platformCompat, @NonNull UserManagerService userManager,
+ @NonNull DomainVerificationManagerInternal domainVerificationManager,
+ @NonNull UserNeedsBadgingCache userNeedsBadgingCache,
+ @NonNull Supplier<ResolveInfo> resolveInfoSupplier,
+ @NonNull Supplier<ActivityInfo> instantAppInstallerActivitySupplier) {
+ mContext = context;
mPreferredActivityHelper = preferredActivityHelper;
+ mPlatformCompat = platformCompat;
+ mUserManager = userManager;
+ mDomainVerificationManager = domainVerificationManager;
+ mUserNeedsBadging = userNeedsBadgingCache;
+ mResolveInfoSupplier = resolveInfoSupplier;
+ mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier;
}
/**
@@ -74,35 +105,33 @@
* However, if {@code resolveForStart} is {@code true}, all instant apps are visible
* since we need to allow the system to start any installed application.
*/
- public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
+ public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
@PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
- if (!mPm.mUserManager.exists(userId)) return null;
+ if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = mPm.updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
- mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+ flags = computer.updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+ computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
resolvedType, flags));
- mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ computer.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "resolve intent");
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
- final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
- flags, privateResolveFlags, filterCallingUid, userId, resolveForStart,
- true /*allowDynamicSplits*/);
+ final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
+ resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
+ resolveForStart, true /*allowDynamicSplits*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final boolean queryMayBeFiltered =
UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
&& !resolveForStart;
- final ResolveInfo bestChoice =
- chooseBestActivity(
- intent, resolvedType, flags, privateResolveFlags, query, userId,
- queryMayBeFiltered);
+ final ResolveInfo bestChoice = chooseBestActivity(computer, intent, resolvedType, flags,
+ privateResolveFlags, query, userId, queryMayBeFiltered);
final boolean nonBrowserOnly =
(privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
@@ -114,7 +143,7 @@
}
}
- private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
+ private ResolveInfo chooseBestActivity(Computer computer, Intent intent, String resolvedType,
@PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
@@ -156,9 +185,10 @@
// If we have an ephemeral app, use it
if (ri.activityInfo.applicationInfo.isInstantApp()) {
final String packageName = ri.activityInfo.packageName;
- final PackageStateInternal ps = mPm.getPackageStateInternal(packageName);
+ final PackageStateInternal ps =
+ computer.getPackageStateInternal(packageName);
if (ps != null && PackageManagerServiceUtils.hasAnyDomainApproval(
- mPm.mDomainVerificationManager, ps, intent, flags, userId)) {
+ mDomainVerificationManager, ps, intent, flags, userId)) {
return ri;
}
}
@@ -167,7 +197,7 @@
& PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
return null;
}
- ri = new ResolveInfo(mPm.getResolveInfo());
+ ri = new ResolveInfo(mResolveInfoSupplier.get());
// if all resolve options are browsers, mark the resolver's info as if it were
// also a browser.
ri.handleAllWebDataURI = browserCount == n;
@@ -184,7 +214,7 @@
if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
ri.resolvePackageName = intentPackage;
- if (mPm.userNeedsBadging(userId)) {
+ if (mUserNeedsBadging.get(userId)) {
ri.noResourceId = true;
} else {
ri.icon = appi.icon;
@@ -225,13 +255,14 @@
return true;
}
- public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
- String featureId, int userId) throws RemoteException {
+ public IntentSender getLaunchIntentSenderForPackage(@NonNull Computer computer,
+ String packageName, String callingPackage, String featureId, int userId)
+ throws RemoteException {
Objects.requireNonNull(packageName);
final int callingUid = Binder.getCallingUid();
- mPm.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ computer.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
false /* checkShell */, "get launch intent sender for package");
- final int packageUid = mPm.getPackageUid(callingPackage, 0 /* flags */, userId);
+ final int packageUid = computer.getPackageUid(callingPackage, 0 /* flags */, userId);
if (!UserHandle.isSameApp(callingUid, packageUid)) {
throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
+ callingUid + " does not own package: " + callingPackage);
@@ -242,17 +273,17 @@
final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_INFO);
intentToResolve.setPackage(packageName);
- String resolvedType = intentToResolve.resolveTypeIfNeeded(
- mPm.mContext.getContentResolver());
- List<ResolveInfo> ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ String resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver);
+ List<ResolveInfo> ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType,
0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
true /* resolveForStart */, false /* allowDynamicSplits */);
if (ris == null || ris.size() <= 0) {
intentToResolve.removeCategory(Intent.CATEGORY_INFO);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
intentToResolve.setPackage(packageName);
- resolvedType = intentToResolve.resolveTypeIfNeeded(mPm.mContext.getContentResolver());
- ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver);
+ ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType,
0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
true /* resolveForStart */, false /* allowDynamicSplits */);
}
@@ -277,17 +308,17 @@
// In this method, we have to know the actual calling UID, but in some cases Binder's
// call identity is removed, so the UID has to be passed in explicitly.
- public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
+ public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Computer computer, Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
int filterCallingUid) {
- if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
- mPm.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
+ if (!mUserManager.exists(userId)) return Collections.emptyList();
+ computer.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "query intent receivers");
- final String instantAppPkgName = mPm.getInstantAppPackageName(filterCallingUid);
- flags = mPm.updateFlagsForResolve(
- flags, userId, filterCallingUid, false /*includeInstantApps*/,
- mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
- flags));
+ final String instantAppPkgName = computer.getInstantAppPackageName(filterCallingUid);
+ flags = computer.updateFlagsForResolve(flags, userId, filterCallingUid,
+ false /*includeInstantApps*/,
+ computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+ resolvedType, flags));
Intent originalIntent = null;
ComponentName comp = intent.getComponent();
if (comp == null) {
@@ -297,9 +328,10 @@
comp = intent.getComponent();
}
}
+ final ComponentResolverApi componentResolver = computer.getComponentResolver();
List<ResolveInfo> list = Collections.emptyList();
if (comp != null) {
- final ActivityInfo ai = mPm.getReceiverInfo(comp, flags, userId);
+ final ActivityInfo ai = computer.getReceiverInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
// used when either 1) the calling package is normal and the activity is within
@@ -335,28 +367,25 @@
list = new ArrayList<>(1);
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
- list, true, intent, resolvedType, filterCallingUid);
+ mPlatformCompat, componentResolver, list, true, intent,
+ resolvedType, filterCallingUid);
}
}
} else {
- // reader
- synchronized (mPm.mLock) {
- String pkgName = intent.getPackage();
- if (pkgName == null) {
- final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
- intent, resolvedType, flags, userId);
- if (result != null) {
- list = result;
- }
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ final List<ResolveInfo> result = componentResolver
+ .queryReceivers(computer, intent, resolvedType, flags, userId);
+ if (result != null) {
+ list = result;
}
- final AndroidPackage pkg = mPm.mPackages.get(pkgName);
- if (pkg != null) {
- final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
- intent, resolvedType, flags, pkg.getReceivers(), userId);
- if (result != null) {
- list = result;
- }
+ }
+ final AndroidPackage pkg = computer.getPackage(pkgName);
+ if (pkg != null) {
+ final List<ResolveInfo> result = componentResolver.queryReceivers(computer,
+ intent, resolvedType, flags, pkg.getReceivers(), userId);
+ if (result != null) {
+ list = result;
}
}
}
@@ -364,21 +393,22 @@
if (originalIntent != null) {
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
+ mPlatformCompat, componentResolver,
list, true, originalIntent, resolvedType, filterCallingUid);
}
- return mPm.applyPostResolutionFilter(
- list, instantAppPkgName, false, filterCallingUid, false, userId, intent);
+ return computer.applyPostResolutionFilter(list, instantAppPkgName, false, filterCallingUid,
+ false, userId, intent);
}
- public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
- if (!mPm.mUserManager.exists(userId)) return null;
- flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ public ResolveInfo resolveServiceInternal(@NonNull Computer computer, Intent intent,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
+ int callingUid) {
+ if (!mUserManager.exists(userId)) return null;
+ flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
- List<ResolveInfo> query = mPm.queryIntentServicesInternal(
+ List<ResolveInfo> query = computer.queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
if (query.size() >= 1) {
@@ -391,12 +421,12 @@
}
public @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
- Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
- int userId) {
- if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+ @NonNull Computer computer, Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- final String instantAppPkgName = mPm.getInstantAppPackageName(callingUid);
- flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ final String instantAppPkgName = computer.getInstantAppPackageName(callingUid);
+ flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
ComponentName comp = intent.getComponent();
if (comp == null) {
@@ -407,7 +437,7 @@
}
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
- final ProviderInfo pi = mPm.getProviderInfo(comp, flags, userId);
+ final ProviderInfo pi = computer.getProviderInfo(comp, flags, userId);
if (pi != null) {
// When specifying an explicit component, we prevent the provider from being
// used when either 1) the provider is in an instant application and the
@@ -432,8 +462,8 @@
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
- && mPm.shouldFilterApplication(
- mPm.getPackageStateInternal(pi.applicationInfo.packageName,
+ && computer.shouldFilterApplication(
+ computer.getPackageStateInternal(pi.applicationInfo.packageName,
Process.SYSTEM_UID), callingUid, userId);
if (!blockResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
@@ -444,46 +474,40 @@
return list;
}
- // reader
- synchronized (mPm.mLock) {
- String pkgName = intent.getPackage();
- if (pkgName == null) {
- final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
- resolvedType, flags, userId);
- if (resolveInfos == null) {
- return Collections.emptyList();
- }
- return applyPostContentProviderResolutionFilter(
- resolveInfos, instantAppPkgName, userId, callingUid);
+ final ComponentResolverApi componentResolver = computer.getComponentResolver();
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
+ intent, resolvedType, flags, userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
}
- final AndroidPackage pkg = mPm.mPackages.get(pkgName);
- if (pkg != null) {
- final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
- resolvedType, flags,
- pkg.getProviders(), userId);
- if (resolveInfos == null) {
- return Collections.emptyList();
- }
- return applyPostContentProviderResolutionFilter(
- resolveInfos, instantAppPkgName, userId, callingUid);
- }
- return Collections.emptyList();
+ return applyPostContentProviderResolutionFilter(computer, resolveInfos,
+ instantAppPkgName, userId, callingUid);
}
+ final AndroidPackage pkg = computer.getPackage(pkgName);
+ if (pkg != null) {
+ final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
+ intent, resolvedType, flags, pkg.getProviders(), userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
+ }
+ return applyPostContentProviderResolutionFilter(computer, resolveInfos,
+ instantAppPkgName, userId, callingUid);
+ }
+ return Collections.emptyList();
}
- private List<ResolveInfo> applyPostContentProviderResolutionFilter(
+ private List<ResolveInfo> applyPostContentProviderResolutionFilter(@NonNull Computer computer,
List<ResolveInfo> resolveInfos, String instantAppPkgName,
@UserIdInt int userId, int callingUid) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
if (instantAppPkgName == null) {
- SettingBase callingSetting =
- mPm.mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
- PackageStateInternal resolvedSetting =
- mPm.getPackageStateInternal(info.providerInfo.packageName, 0);
- if (!mPm.mAppsFilter.shouldFilterApplication(
- callingUid, callingSetting, resolvedSetting, userId)) {
+ PackageStateInternal resolvedSetting = computer.getPackageStateInternal(
+ info.providerInfo.packageName, 0);
+ if (!computer.shouldFilterApplication(resolvedSetting, callingUid, userId)) {
continue;
}
}
@@ -494,7 +518,7 @@
if (info.providerInfo.splitName != null
&& !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
info.providerInfo.splitName)) {
- if (mPm.mInstantAppInstallerActivity == null) {
+ if (mInstantAppInstallerActivitySupplier.get() == null) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
}
@@ -507,7 +531,7 @@
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(
- mPm.getInstantAppInstallerInfo());
+ computer.getInstantAppInstallerInfo());
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
null /*failureActivity*/,
info.providerInfo.packageName,
@@ -531,20 +555,21 @@
return resolveInfos;
}
- public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
- Intent[] specifics, String[] specificTypes, Intent intent,
+ public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(Computer computer,
+ ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+ if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
- mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
- flags));
- mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ flags = computer.updateFlagsForResolve(flags, userId, callingUid,
+ false /*includeInstantApps*/,
+ computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+ resolvedType, flags));
+ computer.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "query intent activity options");
final String resultsAction = intent.getAction();
- final List<ResolveInfo> results = mPm.queryIntentActivitiesInternal(intent, resolvedType,
- flags | PackageManager.GET_RESOLVED_FILTER, userId);
+ final List<ResolveInfo> results = computer.queryIntentActivitiesInternal(intent,
+ resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId);
if (DEBUG_INTENT_MATCHING) {
Log.v(TAG, "Query " + intent + ": " + results);
@@ -584,21 +609,20 @@
ComponentName comp = sintent.getComponent();
if (comp == null) {
- ri = mPm.resolveIntent(
- sintent,
- specificTypes != null ? specificTypes[i] : null,
- flags, userId);
+ ri = resolveIntentInternal(computer, sintent,
+ specificTypes != null ? specificTypes[i] : null, flags,
+ 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
if (ri == null) {
continue;
}
- if (ri == mPm.getResolveInfo()) {
+ if (ri == mResolveInfoSupplier.get()) {
// ACK! Must do something better with this.
}
ai = ri.activityInfo;
comp = new ComponentName(ai.applicationInfo.packageName,
ai.name);
} else {
- ai = mPm.getActivityInfo(comp, flags, userId);
+ ai = computer.getActivityInfo(comp, flags, userId);
if (ai == null) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4d7da1b..2ad35b7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -120,6 +120,7 @@
import com.android.server.pm.pkg.component.ParsedPermission;
import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.resolution.ComponentResolver;
import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationPersistence;
@@ -1551,7 +1552,7 @@
if (pa.mPref.getParseError() == null) {
final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId);
if (resolver.shouldAddPreferredActivity(pa)) {
- resolver.addFilter(pa);
+ resolver.addFilter(null, pa);
}
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -1579,7 +1580,7 @@
String tagName = parser.getName();
if (tagName.equals(TAG_ITEM)) {
PersistentPreferredActivity ppa = new PersistentPreferredActivity(parser);
- editPersistentPreferredActivitiesLPw(userId).addFilter(ppa);
+ editPersistentPreferredActivitiesLPw(userId).addFilter(null, ppa);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <" + TAG_PERSISTENT_PREFERRED_ACTIVITIES + ">: "
@@ -1601,7 +1602,7 @@
final String tagName = parser.getName();
if (tagName.equals(TAG_ITEM)) {
CrossProfileIntentFilter cpif = new CrossProfileIntentFilter(parser);
- editCrossProfileIntentResolverLPw(userId).addFilter(cpif);
+ editCrossProfileIntentResolverLPw(userId).addFilter(null, cpif);
} else {
String msg = "Unknown element under " + TAG_CROSS_PROFILE_INTENT_FILTERS + ": " +
tagName;
@@ -1868,8 +1869,7 @@
.build();
}
if (suspended && suspendParamsMap == null) {
- final SuspendParams suspendParams =
- SuspendParams.getInstanceOrNull(
+ final SuspendParams suspendParams = new SuspendParams(
oldSuspendDialogInfo,
suspendedAppExtras,
suspendedLauncherExtras);
@@ -3517,7 +3517,7 @@
removeFilters(pir, filter, existing);
}
PreferredActivity pa = new PreferredActivity(filter, systemMatch, set, cn, true);
- pir.addFilter(pa);
+ pir.addFilter(null, pa);
} else if (haveNonSys == null) {
StringBuilder sb = new StringBuilder();
sb.append("No component ");
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 85d1367..bd1c9c7 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -112,7 +112,7 @@
}
final SuspendParams newSuspendParams =
- SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+ new SuspendParams(dialogInfo, appExtras, launcherExtras);
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
@@ -148,13 +148,12 @@
final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
packageState.getUserStateOrDefault(userId).getSuspendParams();
- final SuspendParams suspendParams = suspendParamsMap == null
- ? null : suspendParamsMap.get(packageName);
- boolean hasSuspension = suspendParams != null;
if (suspended) {
- if (hasSuspension) {
+ if (suspendParamsMap != null && suspendParamsMap.containsKey(packageName)) {
+ final SuspendParams suspendParams = suspendParamsMap.get(packageName);
// Skip if there's no changes
- if (Objects.equals(suspendParams.getDialogInfo(), dialogInfo)
+ if (suspendParams != null
+ && Objects.equals(suspendParams.getDialogInfo(), dialogInfo)
&& Objects.equals(suspendParams.getAppExtras(), appExtras)
&& Objects.equals(suspendParams.getLauncherExtras(),
launcherExtras)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5c4d011..b7c55c5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -36,6 +36,7 @@
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.StatsManager;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
@@ -98,6 +99,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.StatsEvent;
import android.util.TimeUtils;
import android.util.TypedValue;
import android.util.TypedXmlPullParser;
@@ -608,6 +610,8 @@
if (mUms.mPm.isDeviceUpgrading()) {
mUms.cleanupPreCreatedUsers();
}
+
+ mUms.registerStatsCallbacks();
}
}
@@ -4279,6 +4283,56 @@
: FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
}
+ /** Register callbacks for statsd pulled atoms. */
+ private void registerStatsCallbacks() {
+ final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.USER_INFO,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(),
+ this::onPullAtom);
+ }
+
+ /** Writes a UserInfo pulled atom for each user on the device. */
+ private int onPullAtom(int atomTag, List<StatsEvent> data) {
+ if (atomTag != FrameworkStatsLog.USER_INFO) {
+ Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
+ return android.app.StatsManager.PULL_SKIP;
+ }
+ final List<UserInfo> users = getUsersInternal(true, true, true);
+ final int size = users.size();
+ for (int idx = 0; idx < size; idx++) {
+ final UserInfo user = users.get(idx);
+ if (user.id == UserHandle.USER_SYSTEM) {
+ // Skip user 0. It's not interesting. We already know it exists, is running, and (if
+ // we know the device configuration) its userType.
+ continue;
+ }
+
+ final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
+ final String userTypeCustom = (userTypeStandard ==
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ?
+ user.userType : null;
+
+ boolean isUserRunningUnlocked;
+ synchronized (mUserStates) {
+ isUserRunningUnlocked =
+ mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+ }
+
+ data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
+ user.id,
+ userTypeStandard,
+ userTypeCustom,
+ user.flags,
+ user.creationTime,
+ user.lastLoggedInTime,
+ isUserRunningUnlocked
+ ));
+ }
+ return android.app.StatsManager.PULL_SUCCESS;
+ }
+
@VisibleForTesting
UserData putUserInfo(UserInfo userInfo) {
final UserData userData = new UserData();
diff --git a/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java b/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java
new file mode 100644
index 0000000..90c9228
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+public class UserNeedsBadgingCache {
+
+ private final Object mLock = new Object();
+
+ // Cache of users who need badging.
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseBooleanArray mUserCache = new SparseBooleanArray();
+
+ @NonNull
+ private final UserManagerService mUserManager;
+
+ public UserNeedsBadgingCache(@NonNull UserManagerService userManager) {
+ mUserManager = userManager;
+ }
+
+ public void delete(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mUserCache.delete(userId);
+ }
+ }
+
+ public boolean get(@UserIdInt int userId) {
+ synchronized (mLock) {
+ int index = mUserCache.indexOfKey(userId);
+ if (index >= 0) {
+ return mUserCache.valueAt(index);
+ }
+ }
+
+ final UserInfo userInfo;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ userInfo = mUserManager.getUserInfo(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ final boolean b;
+ b = userInfo != null && userInfo.isManagedProfile();
+ synchronized (mLock) {
+ mUserCache.put(userId, b);
+ }
+ return b;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/WatchedIntentResolver.java b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
index 1c3d884..daa50ab 100644
--- a/services/core/java/com/android/server/pm/WatchedIntentResolver.java
+++ b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
@@ -18,8 +18,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.IntentFilter;
import com.android.server.IntentResolver;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
@@ -99,8 +101,8 @@
}
@Override
- public void addFilter(F f) {
- super.addFilter(f);
+ public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
+ super.addFilter(snapshot, f);
f.registerObserver(mWatcher);
onChanged();
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index ed47bfb7..87494a6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3364,7 +3364,7 @@
// For updated system applications, a privileged/oem permission
// is granted only if it had been defined by the original application.
if (pkgSetting.getTransientState().isUpdatedSystemApp()) {
- final PackageSetting disabledPs = mPackageManagerInt
+ final PackageStateInternal disabledPs = mPackageManagerInt
.getDisabledSystemPackage(pkg.getPackageName());
final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.getPkg();
if (disabledPkg != null
@@ -3762,7 +3762,7 @@
return;
}
- PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
+ PackageStateInternal disabledPs = mPackageManagerInt.getDisabledSystemPackage(
pkg.getPackageName());
boolean isShadowingSystemPkg = disabledPs != null && disabledPs.getAppId() == pkg.getUid();
@@ -4104,7 +4104,7 @@
}
private boolean isPermissionDeclaredByDisabledSystemPkg(@NonNull Permission permission) {
- final PackageSetting disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
+ final PackageStateInternal disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
permission.getPackageName());
if (disabledSourcePs != null && disabledSourcePs.getPkg() != null) {
final String permissionName = permission.getName();
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index d24ce96..0926ba2 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -46,27 +46,13 @@
private final PersistableBundle appExtras;
private final PersistableBundle launcherExtras;
- private SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
+ public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
PersistableBundle launcherExtras) {
this.dialogInfo = dialogInfo;
this.appExtras = appExtras;
this.launcherExtras = launcherExtras;
}
- /**
- * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all
- * the fields are {@code null}.
- *
- * @return A {@link SuspendParams} object or {@code null}.
- */
- public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo,
- PersistableBundle appExtras, PersistableBundle launcherExtras) {
- if (dialogInfo == null && appExtras == null && launcherExtras == null) {
- return null;
- }
- return new SuspendParams(dialogInfo, appExtras, launcherExtras);
- }
-
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
@@ -170,7 +156,7 @@
Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
+ " some fields may default", e);
}
- return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
+ return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras);
}
public SuspendDialogInfo getDialogInfo() {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
similarity index 70%
rename from services/core/java/com/android/server/pm/ComponentResolver.java
rename to services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index aa393d2..cf9370d 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2022 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,16 +14,16 @@
* limitations under the License.
*/
-package com.android.server.pm;
+package com.android.server.pm.resolution;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
-import static android.content.pm.PackageManagerInternal.PACKAGE_SETUP_WIZARD;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -32,11 +32,9 @@
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.InstantAppResolveInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
@@ -46,14 +44,18 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
+import com.android.server.pm.Computer;
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerException;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.UserNeedsBadgingCache;
import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ParsedActivity;
@@ -63,12 +65,13 @@
import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
-import com.android.server.utils.WatchableImpl;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
@@ -79,9 +82,7 @@
import java.util.function.Function;
/** Resolves all Android component types [activities, services, providers and receivers]. */
-public class ComponentResolver
- extends WatchableImpl
- implements Snappable {
+public class ComponentResolver extends ComponentResolverLocked implements Snappable {
private static final boolean DEBUG = false;
private static final String TAG = "PackageManager";
private static final boolean DEBUG_FILTERS = false;
@@ -104,7 +105,7 @@
PROTECTED_ACTIONS.add(Intent.ACTION_VIEW);
}
- static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> {
+ public static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> {
int v1 = r1.priority;
int v2 = r2.priority;
//System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
@@ -140,62 +141,8 @@
return 0;
};
- private static UserManagerService sUserManager;
- private static PackageManagerInternal sPackageManagerInternal;
-
- /**
- * Locking within package manager is going to get worse before it gets better. Currently,
- * we need to share the {@link PackageManagerService} lock to prevent deadlocks. This occurs
- * because in order to safely query the resolvers, we need to obtain this lock. However,
- * during resolution, we call into the {@link PackageManagerService}. This is _not_ to
- * operate on data controlled by the service proper, but, to check the state of package
- * settings [contained in a {@link Settings} object]. However, the {@link Settings} object
- * happens to be protected by the main {@link PackageManagerService} lock.
- * <p>
- * There are a couple potential solutions.
- * <ol>
- * <li>Split all of our locks into reader/writer locks. This would allow multiple,
- * simultaneous read operations and means we don't have to be as cautious about lock
- * layering. Only when we want to perform a write operation will we ever be in a
- * position to deadlock the system.</li>
- * <li>Use the same lock across all classes within the {@code com.android.server.pm}
- * package. By unifying the lock object, we remove any potential lock layering issues
- * within the package manager. However, we already have a sense that this lock is
- * heavily contended and merely adding more dependencies on it will have further
- * impact.</li>
- * <li>Implement proper lock ordering within the package manager. By defining the
- * relative layer of the component [eg. {@link PackageManagerService} is at the top.
- * Somewhere in the middle would be {@link ComponentResolver}. At the very bottom
- * would be {@link Settings}.] The ordering would allow higher layers to hold their
- * lock while calling down. Lower layers must relinquish their lock before calling up.
- * Since {@link Settings} would live at the lowest layer, the {@link ComponentResolver}
- * would be able to hold its lock while checking the package setting state.</li>
- * </ol>
- */
- private final PackageManagerTracedLock mLock;
-
- /** All available activities, for your resolving pleasure. */
- @GuardedBy("mLock")
- private final ActivityIntentResolver mActivities;
-
- /** All available providers, for your resolving pleasure. */
- @GuardedBy("mLock")
- private final ProviderIntentResolver mProviders;
-
- /** All available receivers, for your resolving pleasure. */
- @GuardedBy("mLock")
- private final ReceiverIntentResolver mReceivers;
-
- /** All available services, for your resolving pleasure. */
- @GuardedBy("mLock")
- private final ServiceIntentResolver mServices;
-
- /** Mapping from provider authority [first directory in content URI codePath) to provider. */
- @GuardedBy("mLock")
- private final ArrayMap<String, ParsedProvider> mProvidersByAuthority;
-
/** Whether or not processing protected filters should be deferred. */
- private boolean mDeferProtectedFilters = true;
+ boolean mDeferProtectedFilters = true;
/**
* Tracks high priority intent filters for protected actions. During boot, certain
@@ -210,348 +157,62 @@
* This is a pair of component package name to actual filter, because we don't store the
* name inside the filter. It's technically independent of the component it's contained in.
*/
- private List<Pair<ParsedMainComponent, ParsedIntentInfo>> mProtectedFilters;
+ List<Pair<ParsedMainComponent, ParsedIntentInfo>> mProtectedFilters;
- ComponentResolver(UserManagerService userManager,
- PackageManagerInternal packageManagerInternal,
- PackageManagerTracedLock lock) {
- sPackageManagerInternal = packageManagerInternal;
- sUserManager = userManager;
- mLock = lock;
-
- mActivities = new ActivityIntentResolver();
- mProviders = new ProviderIntentResolver();
- mReceivers = new ReceiverIntentResolver();
- mServices = new ServiceIntentResolver();
+ public ComponentResolver(@NonNull UserManagerService userManager,
+ @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+ super(userManager);
+ mActivities = new ActivityIntentResolver(userManager, userNeedsBadgingCache);
+ mProviders = new ProviderIntentResolver(userManager);
+ mReceivers = new ReceiverIntentResolver(userManager, userNeedsBadgingCache);
+ mServices = new ServiceIntentResolver(userManager);
mProvidersByAuthority = new ArrayMap<>();
mDeferProtectedFilters = true;
- mSnapshot = new SnapshotCache<ComponentResolver>(this, this) {
+ mSnapshot = new SnapshotCache<ComponentResolverApi>(this, this) {
@Override
- public ComponentResolver createSnapshot() {
- return new ComponentResolver(mSource);
+ public ComponentResolverApi createSnapshot() {
+ return new ComponentResolverSnapshot(ComponentResolver.this,
+ userNeedsBadgingCache);
}};
}
- // Copy constructor used in creating snapshots.
- private ComponentResolver(ComponentResolver orig) {
- // Do not set the static variables that are set in the default constructor. Do
- // create a new object for the lock. The snapshot is read-only, so a lock is not
- // strictly required. However, the current code is simpler if the lock exists,
- // but does not contend with any outside class.
- // TODO: make the snapshot lock-free
- mLock = new PackageManagerTracedLock();
-
- mActivities = new ActivityIntentResolver(orig.mActivities);
- mProviders = new ProviderIntentResolver(orig.mProviders);
- mReceivers = new ReceiverIntentResolver(orig.mReceivers);
- mServices = new ServiceIntentResolver(orig.mServices);
- mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
- mDeferProtectedFilters = orig.mDeferProtectedFilters;
- mProtectedFilters = (mProtectedFilters == null)
- ? null
- : new ArrayList<>(orig.mProtectedFilters);
-
- mSnapshot = null;
- }
-
- final SnapshotCache<ComponentResolver> mSnapshot;
+ final SnapshotCache<ComponentResolverApi> mSnapshot;
/**
* Create a snapshot.
*/
- public ComponentResolver snapshot() {
+ public ComponentResolverApi snapshot() {
return mSnapshot.snapshot();
}
-
- /** Returns the given activity */
- @Nullable
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public ParsedActivity getActivity(@NonNull ComponentName component) {
- synchronized (mLock) {
- return mActivities.mActivities.get(component);
- }
- }
-
- /** Returns the given provider */
- @Nullable
- ParsedProvider getProvider(@NonNull ComponentName component) {
- synchronized (mLock) {
- return mProviders.mProviders.get(component);
- }
- }
-
- /** Returns the given receiver */
- @Nullable
- ParsedActivity getReceiver(@NonNull ComponentName component) {
- synchronized (mLock) {
- return mReceivers.mActivities.get(component);
- }
- }
-
- /** Returns the given service */
- @Nullable
- ParsedService getService(@NonNull ComponentName component) {
- synchronized (mLock) {
- return mServices.mServices.get(component);
- }
- }
-
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean componentExists(@NonNull ComponentName componentName) {
- synchronized (mLock) {
- ParsedMainComponent component = mActivities.mActivities.get(componentName);
- if (component != null) {
- return true;
- }
- component = mReceivers.mActivities.get(componentName);
- if (component != null) {
- return true;
- }
- component = mServices.mServices.get(componentName);
- if (component != null) {
- return true;
- }
- return mProviders.mProviders.get(componentName) != null;
- }
- }
-
- @Nullable
- List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
- int userId) {
- synchronized (mLock) {
- return mActivities.queryIntent(intent, resolvedType, flags, userId);
- }
- }
-
- @Nullable
- List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
- List<ParsedActivity> activities, int userId) {
- synchronized (mLock) {
- return mActivities.queryIntentForPackage(
- intent, resolvedType, flags, activities, userId);
- }
- }
-
- @Nullable
- List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags, int userId) {
- synchronized (mLock) {
- return mProviders.queryIntent(intent, resolvedType, flags, userId);
- }
- }
-
- @Nullable
- List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags,
- List<ParsedProvider> providers, int userId) {
- synchronized (mLock) {
- return mProviders.queryIntentForPackage(intent, resolvedType, flags, providers, userId);
- }
- }
-
- @Nullable
- List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, long flags,
- int userId) {
- if (!sUserManager.exists(userId)) {
- return null;
- }
- List<ProviderInfo> providerList = null;
- CachedApplicationInfoGenerator appInfoGenerator = null;
- synchronized (mLock) {
- for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
- final ParsedProvider p = mProviders.mProviders.valueAt(i);
- if (p.getAuthority() == null) {
- continue;
- }
-
- final PackageStateInternal ps =
- sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
- if (ps == null) {
- continue;
- }
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
- if (pkg == null) {
- continue;
- }
-
- if (processName != null && (!p.getProcessName().equals(processName)
- || !UserHandle.isSameApp(pkg.getUid(), uid))) {
- continue;
- }
- // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
- if (metaDataKey != null && !p.getMetaData().containsKey(metaDataKey)) {
- continue;
- }
- if (appInfoGenerator == null) {
- appInfoGenerator = new CachedApplicationInfoGenerator();
- }
- final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
- final ApplicationInfo appInfo =
- appInfoGenerator.generate(pkg, flags, state, userId, ps);
- if (appInfo == null) {
- continue;
- }
-
- final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
- pkg, p, flags, state, appInfo, userId, ps);
- if (info == null) {
- continue;
- }
- if (providerList == null) {
- providerList = new ArrayList<>(i + 1);
- }
- providerList.add(info);
- }
- }
- return providerList;
- }
-
- @Nullable
- ProviderInfo queryProvider(String authority, long flags, int userId) {
- synchronized (mLock) {
- final ParsedProvider p = mProvidersByAuthority.get(authority);
- if (p == null) {
- return null;
- }
- final PackageStateInternal ps =
- sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
- if (ps == null) {
- return null;
- }
- final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
- if (pkg == null) {
- return null;
- }
- final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
- ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
- pkg, flags, state, userId, ps);
- if (appInfo == null) {
- return null;
- }
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps);
- }
- }
-
- void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode,
- int userId) {
- synchronized (mLock) {
- CachedApplicationInfoGenerator appInfoGenerator = null;
- for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
- final ParsedProvider p = mProvidersByAuthority.valueAt(i);
- if (!p.isSyncable()) {
- continue;
- }
-
- final PackageStateInternal ps =
- sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
- if (ps == null) {
- continue;
- }
-
- final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
- if (pkg == null) {
- continue;
- }
-
- if (safeMode && !pkg.isSystem()) {
- continue;
- }
- if (appInfoGenerator == null) {
- appInfoGenerator = new CachedApplicationInfoGenerator();
- }
- final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
- final ApplicationInfo appInfo =
- appInfoGenerator.generate(pkg, 0, state, userId, ps);
- if (appInfo == null) {
- continue;
- }
-
- final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
- pkg, p, 0, state, appInfo, userId, ps);
- if (info == null) {
- continue;
- }
- outNames.add(mProvidersByAuthority.keyAt(i));
- outInfo.add(info);
- }
- }
- }
-
- @Nullable
- List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags, int userId) {
- synchronized (mLock) {
- return mReceivers.queryIntent(intent, resolvedType, flags, userId);
- }
- }
-
- @Nullable
- List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags,
- List<ParsedActivity> receivers, int userId) {
- synchronized (mLock) {
- return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId);
- }
- }
-
- @Nullable
- List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags, int userId) {
- synchronized (mLock) {
- return mServices.queryIntent(intent, resolvedType, flags, userId);
- }
- }
-
- @Nullable
- List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags,
- List<ParsedService> services, int userId) {
- synchronized (mLock) {
- return mServices.queryIntentForPackage(intent, resolvedType, flags, services, userId);
- }
- }
-
- /** Returns {@code true} if the given activity is defined by some package */
- boolean isActivityDefined(ComponentName component) {
- synchronized (mLock) {
- return mActivities.mActivities.get(component) != null;
- }
- }
-
- /** Asserts none of the providers defined in the given package haven't already been defined. */
- void assertProvidersNotDefined(AndroidPackage pkg) throws PackageManagerException {
- synchronized (mLock) {
- assertProvidersNotDefinedLocked(pkg);
- }
- }
-
/** Add all components defined in the given package to the internal structures. */
- void addAllComponents(AndroidPackage pkg, boolean chatty) {
+ public void addAllComponents(AndroidPackage pkg, boolean chatty,
+ @Nullable String setupWizardPackage, @NonNull Computer computer) {
final ArrayList<Pair<ParsedActivity, ParsedIntentInfo>> newIntents = new ArrayList<>();
synchronized (mLock) {
- addActivitiesLocked(pkg, newIntents, chatty);
- addReceiversLocked(pkg, chatty);
- addProvidersLocked(pkg, chatty);
- addServicesLocked(pkg, chatty);
+ addActivitiesLocked(computer, pkg, newIntents, chatty);
+ addReceiversLocked(computer, pkg, chatty);
+ addProvidersLocked(computer, pkg, chatty);
+ addServicesLocked(computer, pkg, chatty);
onChanged();
}
- // expect single setupwizard package
- final String setupWizardPackage = ArrayUtils.firstOrNull(
- sPackageManagerInternal.getKnownPackageNames(
- PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
for (int i = newIntents.size() - 1; i >= 0; --i) {
final Pair<ParsedActivity, ParsedIntentInfo> pair = newIntents.get(i);
- final PackageSetting disabledPkgSetting = (PackageSetting) sPackageManagerInternal
+ final PackageStateInternal disabledPkgSetting = computer
.getDisabledSystemPackage(pair.first.getPackageName());
final AndroidPackage disabledPkg =
disabledPkgSetting == null ? null : disabledPkgSetting.getPkg();
final List<ParsedActivity> systemActivities =
disabledPkg != null ? disabledPkg.getActivities() : null;
- adjustPriority(systemActivities, pair.first, pair.second, setupWizardPackage);
+ adjustPriority(computer, systemActivities, pair.first, pair.second, setupWizardPackage);
onChanged();
}
}
/** Removes all components defined in the given package from the internal structures. */
- void removeAllComponents(AndroidPackage pkg, boolean chatty) {
+ public void removeAllComponents(AndroidPackage pkg, boolean chatty) {
synchronized (mLock) {
removeAllComponentsLocked(pkg, chatty);
onChanged();
@@ -562,7 +223,7 @@
* Reprocess any protected filters that have been deferred. At this point, we've scanned
* all of the filters defined on the /system partition and know the special components.
*/
- void fixProtectedFilterPriorities() {
+ public void fixProtectedFilterPriorities(@Nullable String setupWizardPackage) {
synchronized (mLock) {
if (!mDeferProtectedFilters) {
return;
@@ -576,11 +237,6 @@
mProtectedFilters;
mProtectedFilters = null;
- // expect single setupwizard package
- final String setupWizardPackage = ArrayUtils.firstOrNull(
- sPackageManagerInternal.getKnownPackageNames(
- PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
-
if (DEBUG_FILTERS && setupWizardPackage == null) {
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
@@ -615,7 +271,7 @@
}
}
- void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+ public void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
synchronized (mLock) {
if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
: "Activity Resolver Table:", " ", packageName,
@@ -625,7 +281,7 @@
}
}
- void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+ public void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
synchronized (mLock) {
if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
: "Provider Resolver Table:", " ", packageName,
@@ -635,7 +291,7 @@
}
}
- void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+ public void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
synchronized (mLock) {
if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
: "Receiver Resolver Table:", " ", packageName,
@@ -645,7 +301,7 @@
}
}
- void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+ public void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
synchronized (mLock) {
if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
: "Service Resolver Table:", " ", packageName,
@@ -655,7 +311,8 @@
}
}
- void dumpContentProviders(PrintWriter pw, DumpState dumpState, String packageName) {
+ public void dumpContentProviders(@NonNull Computer computer, PrintWriter pw,
+ DumpState dumpState, String packageName) {
synchronized (mLock) {
boolean printedSomething = false;
for (ParsedProvider p : mProviders.mProviders.values()) {
@@ -695,7 +352,7 @@
pw.print(" ");
pw.println(p.toString());
- AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+ AndroidPackage pkg = computer.getPackage(p.getPackageName());
if (pkg != null) {
pw.print(" applicationInfo=");
@@ -705,7 +362,7 @@
}
}
- void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
+ public void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
synchronized (mLock) {
if (dumpState.onTitlePrinted()) pw.println();
pw.println("Service permissions:");
@@ -728,13 +385,13 @@
}
@GuardedBy("mLock")
- private void addActivitiesLocked(AndroidPackage pkg,
+ private void addActivitiesLocked(@NonNull Computer computer, AndroidPackage pkg,
List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents, boolean chatty) {
final int activitiesSize = ArrayUtils.size(pkg.getActivities());
StringBuilder r = null;
for (int i = 0; i < activitiesSize; i++) {
ParsedActivity a = pkg.getActivities().get(i);
- mActivities.addActivity(a, "activity", newIntents);
+ mActivities.addActivity(computer, a, "activity", newIntents);
if (DEBUG_PACKAGE_SCANNING && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -750,12 +407,12 @@
}
@GuardedBy("mLock")
- private void addProvidersLocked(AndroidPackage pkg, boolean chatty) {
+ private void addProvidersLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
final int providersSize = ArrayUtils.size(pkg.getProviders());
StringBuilder r = null;
for (int i = 0; i < providersSize; i++) {
ParsedProvider p = pkg.getProviders().get(i);
- mProviders.addProvider(p);
+ mProviders.addProvider(computer, p);
if (p.getAuthority() != null) {
String[] names = p.getAuthority().split(";");
@@ -814,12 +471,12 @@
}
@GuardedBy("mLock")
- private void addReceiversLocked(AndroidPackage pkg, boolean chatty) {
+ private void addReceiversLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
final int receiversSize = ArrayUtils.size(pkg.getReceivers());
StringBuilder r = null;
for (int i = 0; i < receiversSize; i++) {
ParsedActivity a = pkg.getReceivers().get(i);
- mReceivers.addActivity(a, "receiver", null);
+ mReceivers.addActivity(computer, a, "receiver", null);
if (DEBUG_PACKAGE_SCANNING && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -835,12 +492,12 @@
}
@GuardedBy("mLock")
- private void addServicesLocked(AndroidPackage pkg, boolean chatty) {
+ private void addServicesLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
final int servicesSize = ArrayUtils.size(pkg.getServices());
StringBuilder r = null;
for (int i = 0; i < servicesSize; i++) {
ParsedService s = pkg.getServices().get(i);
- mServices.addService(s);
+ mServices.addService(computer, s);
if (DEBUG_PACKAGE_SCANNING && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -945,8 +602,8 @@
* <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is
* allowed to obtain any priority on any action.
*/
- private void adjustPriority(List<ParsedActivity> systemActivities, ParsedActivity activity,
- ParsedIntentInfo intentInfo, String setupWizardPackage) {
+ private void adjustPriority(@NonNull Computer computer, List<ParsedActivity> systemActivities,
+ ParsedActivity activity, ParsedIntentInfo intentInfo, String setupWizardPackage) {
// nothing to do; priority is fine as-is
IntentFilter intentFilter = intentInfo.getIntentFilter();
if (intentFilter.getPriority() <= 0) {
@@ -954,7 +611,7 @@
}
String packageName = activity.getPackageName();
- AndroidPackage pkg = sPackageManagerInternal.getPackage(packageName);
+ AndroidPackage pkg = computer.getPackage(packageName);
final boolean privilegedApp = pkg.isPrivileged();
String className = activity.getClassName();
@@ -1231,28 +888,30 @@
}
}
- @GuardedBy("mLock")
- private void assertProvidersNotDefinedLocked(AndroidPackage pkg)
+ /** Asserts none of the providers defined in the given package haven't already been defined. */
+ public void assertProvidersNotDefined(@NonNull AndroidPackage pkg)
throws PackageManagerException {
- final int providersSize = ArrayUtils.size(pkg.getProviders());
- int i;
- for (i = 0; i < providersSize; i++) {
- ParsedProvider p = pkg.getProviders().get(i);
- if (p.getAuthority() != null) {
- final String[] names = p.getAuthority().split(";");
- for (int j = 0; j < names.length; j++) {
- if (mProvidersByAuthority.containsKey(names[j])) {
- final ParsedProvider other = mProvidersByAuthority.get(names[j]);
- final String otherPackageName =
- (other != null && other.getComponentName() != null)
- ? other.getComponentName().getPackageName() : "?";
- // if we're installing over the same already-installed package, this is ok
- if (!otherPackageName.equals(pkg.getPackageName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_CONFLICTING_PROVIDER,
- "Can't install because provider name " + names[j]
- + " (in package " + pkg.getPackageName()
- + ") is already used by " + otherPackageName);
+ synchronized (mLock) {
+ final int providersSize = ArrayUtils.size(pkg.getProviders());
+ int i;
+ for (i = 0; i < providersSize; i++) {
+ ParsedProvider p = pkg.getProviders().get(i);
+ if (p.getAuthority() != null) {
+ final String[] names = p.getAuthority().split(";");
+ for (int j = 0; j < names.length; j++) {
+ if (mProvidersByAuthority.containsKey(names[j])) {
+ final ParsedProvider other = mProvidersByAuthority.get(names[j]);
+ final String otherPackageName =
+ (other != null && other.getComponentName() != null)
+ ? other.getComponentName().getPackageName() : "?";
+ // if installing over the same already-installed package,this is ok
+ if (!otherPackageName.equals(pkg.getPackageName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_CONFLICTING_PROVIDER,
+ "Can't install because provider name " + names[j]
+ + " (in package " + pkg.getPackageName()
+ + ") is already used by " + otherPackageName);
+ }
}
}
}
@@ -1266,22 +925,31 @@
private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
private boolean mIsUpdatingMimeGroup = false;
+ @NonNull
+ protected final UserManagerService mUserManager;
+
// Default constructor
- MimeGroupsAwareIntentResolver() {
+ MimeGroupsAwareIntentResolver(@NonNull UserManagerService userManager) {
+ mUserManager = userManager;
}
// Copy constructor used in creating snapshots
- MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig) {
+ MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig,
+ @NonNull UserManagerService userManager) {
+ mUserManager = userManager;
copyFrom(orig);
copyInto(mMimeGroupToFilter, orig.mMimeGroupToFilter);
mIsUpdatingMimeGroup = orig.mIsUpdatingMimeGroup;
}
@Override
- public void addFilter(F f) {
+ public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
IntentFilter intentFilter = getIntentFilter(f);
- applyMimeGroups(f);
- super.addFilter(f);
+ // We assume Computer is available for this class and all subclasses. Because this class
+ // uses subclass method override to handle logic, the Computer parameter must be in the
+ // base, leading to this odd nullability.
+ applyMimeGroups((Computer) snapshot, f);
+ super.addFilter(snapshot, f);
if (!mIsUpdatingMimeGroup) {
register_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter,
@@ -1309,7 +977,8 @@
* @param mimeGroup MIME group to update
* @return true, if any intent filters were changed due to this update
*/
- public boolean updateMimeGroup(String packageName, String mimeGroup) {
+ public boolean updateMimeGroup(@NonNull Computer computer, String packageName,
+ String mimeGroup) {
F[] filters = mMimeGroupToFilter.get(mimeGroup);
int n = filters != null ? filters.length : 0;
@@ -1318,18 +987,18 @@
F filter;
for (int i = 0; i < n && (filter = filters[i]) != null; i++) {
if (isPackageForFilter(packageName, filter)) {
- hasChanges |= updateFilter(filter);
+ hasChanges |= updateFilter(computer, filter);
}
}
mIsUpdatingMimeGroup = false;
return hasChanges;
}
- private boolean updateFilter(F f) {
+ private boolean updateFilter(@NonNull Computer computer, F f) {
IntentFilter filter = getIntentFilter(f);
List<String> oldTypes = filter.dataTypes();
removeFilter(f);
- addFilter(f);
+ addFilter(computer, f);
List<String> newTypes = filter.dataTypes();
return !equalLists(oldTypes, newTypes);
}
@@ -1350,16 +1019,18 @@
return first.equals(second);
}
- private void applyMimeGroups(F f) {
+ private void applyMimeGroups(@NonNull Computer computer, F f) {
IntentFilter filter = getIntentFilter(f);
for (int i = filter.countMimeGroups() - 1; i >= 0; i--) {
- List<String> mimeTypes = sPackageManagerInternal.getMimeGroup(
- f.first.getPackageName(), filter.getMimeGroup(i));
+ final PackageStateInternal packageState = computer.getPackageStateInternal(
+ f.first.getPackageName());
- for (int typeIndex = mimeTypes.size() - 1; typeIndex >= 0; typeIndex--) {
- String mimeType = mimeTypes.get(typeIndex);
+ Collection<String> mimeTypes = packageState == null
+ ? Collections.emptyList() : packageState.getMimeGroups()
+ .get(filter.getMimeGroup(i));
+ for (String mimeType : mimeTypes) {
try {
filter.addDynamicDataType(mimeType);
} catch (IntentFilter.MalformedMimeTypeException e) {
@@ -1370,50 +1041,74 @@
}
}
}
+
+ @Override
+ protected boolean isFilterStopped(@Nullable PackageStateInternal packageState,
+ @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) {
+ return true;
+ }
+
+ if (packageState == null || packageState.getPkg() == null) {
+ return false;
+ }
+
+ // System apps are never considered stopped for purposes of
+ // filtering, because there may be no way for the user to
+ // actually re-launch them.
+ return !packageState.isSystem()
+ && packageState.getUserStateOrDefault(userId).isStopped();
+ }
}
- private static class ActivityIntentResolver
+ public static class ActivityIntentResolver
extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
+ @NonNull
+ private UserNeedsBadgingCache mUserNeedsBadging;
+
// Default constructor
- ActivityIntentResolver() {
+ ActivityIntentResolver(@NonNull UserManagerService userManager,
+ @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+ super(userManager);
+ mUserNeedsBadging = userNeedsBadgingCache;
}
// Copy constructor used in creating snapshots
- ActivityIntentResolver(ActivityIntentResolver orig) {
- super(orig);
+ ActivityIntentResolver(@NonNull ActivityIntentResolver orig,
+ @NonNull UserManagerService userManager,
+ @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+ super(orig, userManager);
mActivities.putAll(orig.mActivities);
- mFlags = orig.mFlags;
+ mUserNeedsBadging = userNeedsBadgingCache;
}
@Override
- public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
- boolean defaultOnly, int userId) {
- if (!sUserManager.exists(userId)) return null;
- mFlags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
- return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+ public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
+ return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
}
- List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
- int userId) {
- if (!sUserManager.exists(userId)) {
+ List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, int userId) {
+ if (!mUserManager.exists(userId)) {
return null;
}
- mFlags = flags;
- return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
- userId);
+ return super.queryIntent(computer, intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
}
- List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
- long flags, List<ParsedActivity> packageActivities, int userId) {
- if (!sUserManager.exists(userId)) {
+ List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, List<ParsedActivity> packageActivities,
+ int userId) {
+ if (!mUserManager.exists(userId)) {
return null;
}
if (packageActivities == null) {
return Collections.emptyList();
}
- mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int activitiesSize = packageActivities.size();
ArrayList<Pair<ParsedActivity, ParsedIntentInfo>[]> listCut =
@@ -1431,10 +1126,11 @@
listCut.add(array);
}
}
- return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+ return super.queryIntentFromList(computer, intent, resolvedType,
+ defaultOnly, listCut, userId, flags);
}
- protected void addActivity(ParsedActivity a, String type,
+ protected void addActivity(@NonNull Computer computer, ParsedActivity a, String type,
List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
mActivities.put(a.getComponentName(), a);
if (DEBUG_SHOW_INFO) {
@@ -1455,7 +1151,7 @@
if (!intentFilter.debugCheck()) {
Log.w(TAG, "==> For Activity " + a.getName());
}
- addFilter(Pair.create(a, intent));
+ addFilter(computer, Pair.create(a, intent));
}
}
@@ -1497,11 +1193,6 @@
}
@Override
- protected boolean isFilterStopped(Pair<ParsedActivity, ParsedIntentInfo> filter, int userId) {
- return ComponentResolver.isFilterStopped(filter, userId);
- }
-
- @Override
protected boolean isPackageForFilter(String packageName,
Pair<ParsedActivity, ParsedIntentInfo> info) {
return packageName.equals(info.first.getPackageName());
@@ -1517,43 +1208,35 @@
}
@Override
- protected ResolveInfo newResult(Pair<ParsedActivity, ParsedIntentInfo> pair,
- int match, int userId) {
+ protected ResolveInfo newResult(@NonNull Computer computer,
+ Pair<ParsedActivity, ParsedIntentInfo> pair, int match, int userId,
+ long customFlags) {
ParsedActivity activity = pair.first;
ParsedIntentInfo info = pair.second;
IntentFilter intentFilter = info.getIntentFilter();
- if (!sUserManager.exists(userId)) {
+ if (!mUserManager.exists(userId)) {
if (DEBUG) {
log("User doesn't exist", info, match, userId);
}
return null;
}
- AndroidPackage pkg = sPackageManagerInternal.getPackage(activity.getPackageName());
- if (pkg == null) {
- return null;
- }
-
- if (!sPackageManagerInternal.isEnabledAndMatches(activity, mFlags, userId)) {
+ final PackageStateInternal packageState =
+ computer.getPackageStateInternal(activity.getPackageName());
+ if (packageState == null || packageState.getPkg() == null
+ || !PackageStateUtils.isEnabledAndMatches(packageState, activity, customFlags,
+ userId)) {
if (DEBUG) {
- log("!PackageManagerInternal.isEnabledAndMatches; mFlags="
- + DebugUtils.flagsToString(PackageManager.class, "MATCH_", mFlags),
+ log("!PackageManagerInternal.isEnabledAndMatches; flags="
+ + DebugUtils.flagsToString(PackageManager.class, "MATCH_", customFlags),
info, match, userId);
}
return null;
}
- PackageStateInternal ps =
- sPackageManagerInternal.getPackageStateInternal(activity.getPackageName());
- if (ps == null) {
- if (DEBUG) {
- log("info.activity.owner.mExtras == null", info, match, userId);
- }
- return null;
- }
- final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
- ActivityInfo ai = PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags,
- userState, userId, ps);
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ ActivityInfo ai = PackageInfoUtils.generateActivityInfo(packageState.getPkg(), activity,
+ customFlags, userState, userId, packageState);
if (ai == null) {
if (DEBUG) {
log("Failed to create ActivityInfo based on " + activity, info, match,
@@ -1562,15 +1245,15 @@
return null;
}
final boolean matchExplicitlyVisibleOnly =
- (mFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
+ (customFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
final boolean matchVisibleToInstantApp =
- (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ (customFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean componentVisible =
matchVisibleToInstantApp
&& intentFilter.isVisibleToInstantApp()
&& (!matchExplicitlyVisibleOnly
|| intentFilter.isExplicitlyVisibleToInstantApp());
- final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+ final boolean matchInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
// throw out filters that aren't visible to ephemeral apps
if (matchVisibleToInstantApp && !(componentVisible || userState.isInstantApp())) {
if (DEBUG) {
@@ -1595,7 +1278,7 @@
}
// throw out instant app filters if updates are available; will trigger
// instant app resolution
- if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+ if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
if (DEBUG) {
log("Instant app update is available", info, match, userId);
}
@@ -1604,7 +1287,7 @@
final ResolveInfo res =
new ResolveInfo(intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE));
res.activityInfo = ai;
- if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+ if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = intentFilter;
}
res.handleAllWebDataURI = intentFilter.handleAllWebDataURI();
@@ -1617,7 +1300,7 @@
res.isDefault = info.isHasDefault();
res.labelRes = info.getLabelRes();
res.nonLocalizedLabel = info.getNonLocalizedLabel();
- if (sPackageManagerInternal.userNeedsBadging(userId)) {
+ if (mUserNeedsBadging.get(userId)) {
res.noResourceId = true;
} else {
res.icon = info.getIcon();
@@ -1683,19 +1366,22 @@
// ActivityIntentResolver.
protected final ArrayMap<ComponentName, ParsedActivity> mActivities =
new ArrayMap<>();
- private long mFlags;
}
// Both receivers and activities share a class, but point to different get methods
- private static final class ReceiverIntentResolver extends ActivityIntentResolver {
+ public static final class ReceiverIntentResolver extends ActivityIntentResolver {
// Default constructor
- ReceiverIntentResolver() {
+ ReceiverIntentResolver(@NonNull UserManagerService userManager,
+ @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+ super(userManager, userNeedsBadgingCache);
}
// Copy constructor used in creating snapshots
- ReceiverIntentResolver(ReceiverIntentResolver orig) {
- super(orig);
+ ReceiverIntentResolver(@NonNull ReceiverIntentResolver orig,
+ @NonNull UserManagerService userManager,
+ @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+ super(orig, userManager, userNeedsBadgingCache);
}
@Override
@@ -1704,48 +1390,50 @@
}
}
- private static final class ProviderIntentResolver
+ public static final class ProviderIntentResolver
extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> {
// Default constructor
- ProviderIntentResolver() {
+ ProviderIntentResolver(@NonNull UserManagerService userManager) {
+ super(userManager);
}
// Copy constructor used in creating snapshots
- ProviderIntentResolver(ProviderIntentResolver orig) {
- super(orig);
+ ProviderIntentResolver(@NonNull ProviderIntentResolver orig,
+ @NonNull UserManagerService userManager) {
+ super(orig, userManager);
mProviders.putAll(orig.mProviders);
- mFlags = orig.mFlags;
}
@Override
- public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
- boolean defaultOnly, int userId) {
- mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
- return super.queryIntent(intent, resolvedType, defaultOnly, userId);
- }
-
- @Nullable
- List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
- int userId) {
- if (!sUserManager.exists(userId)) {
+ public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) {
return null;
}
- mFlags = flags;
- return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
- userId);
+ long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+ return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
}
@Nullable
- List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
- long flags, List<ParsedProvider> packageProviders, int userId) {
- if (!sUserManager.exists(userId)) {
+ List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ return super.queryIntent(computer, intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
+ }
+
+ @Nullable
+ List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, List<ParsedProvider> packageProviders,
+ int userId) {
+ if (!mUserManager.exists(userId)) {
return null;
}
if (packageProviders == null) {
return Collections.emptyList();
}
- mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int providersSize = packageProviders.size();
ArrayList<Pair<ParsedProvider, ParsedIntentInfo>[]> listCut =
@@ -1763,10 +1451,11 @@
listCut.add(array);
}
}
- return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+ return super.queryIntentFromList(computer, intent, resolvedType,
+ defaultOnly, listCut, userId, flags);
}
- void addProvider(ParsedProvider p) {
+ void addProvider(@NonNull Computer computer, ParsedProvider p) {
if (mProviders.containsKey(p.getComponentName())) {
Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring");
return;
@@ -1789,7 +1478,7 @@
if (!intentFilter.debugCheck()) {
Log.w(TAG, "==> For Provider " + p.getName());
}
- addFilter(Pair.create(p, intent));
+ addFilter(computer, Pair.create(p, intent));
}
}
@@ -1832,21 +1521,16 @@
}
@Override
- protected boolean isFilterStopped(Pair<ParsedProvider, ParsedIntentInfo> filter,
- int userId) {
- return ComponentResolver.isFilterStopped(filter, userId);
- }
-
- @Override
protected boolean isPackageForFilter(String packageName,
Pair<ParsedProvider, ParsedIntentInfo> info) {
return packageName.equals(info.first.getPackageName());
}
@Override
- protected ResolveInfo newResult(Pair<ParsedProvider, ParsedIntentInfo> pair,
- int match, int userId) {
- if (!sUserManager.exists(userId)) {
+ protected ResolveInfo newResult(@NonNull Computer computer,
+ Pair<ParsedProvider, ParsedIntentInfo> pair, int match, int userId,
+ long customFlags) {
+ if (!mUserManager.exists(userId)) {
return null;
}
@@ -1854,24 +1538,18 @@
ParsedIntentInfo intentInfo = pair.second;
IntentFilter filter = intentInfo.getIntentFilter();
- AndroidPackage pkg = sPackageManagerInternal.getPackage(provider.getPackageName());
- if (pkg == null) {
+ PackageStateInternal packageState =
+ computer.getPackageStateInternal(provider.getPackageName());
+ if (packageState == null || packageState.getPkg() == null
+ || !PackageStateUtils.isEnabledAndMatches(packageState, provider, customFlags,
+ userId)) {
return null;
}
- if (!sPackageManagerInternal.isEnabledAndMatches(provider, mFlags, userId)) {
- return null;
- }
-
- PackageStateInternal ps =
- sPackageManagerInternal.getPackageStateInternal(provider.getPackageName());
- if (ps == null) {
- return null;
- }
- final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
- final boolean matchVisibleToInstantApp = (mFlags
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ final boolean matchVisibleToInstantApp = (customFlags
& PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
- final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+ final boolean isInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
// throw out filters that aren't visible to instant applications
if (matchVisibleToInstantApp
&& !(filter.isVisibleToInstantApp() || userState.isInstantApp())) {
@@ -1883,22 +1561,22 @@
}
// throw out instant application filters if updates are available; will trigger
// instant application resolution
- if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+ if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
return null;
}
final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
- pkg, mFlags, userState, userId, ps);
+ packageState.getPkg(), customFlags, userState, userId, packageState);
if (appInfo == null) {
return null;
}
- ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
- userState, appInfo, userId, ps);
+ ProviderInfo pi = PackageInfoUtils.generateProviderInfo(packageState.getPkg(), provider,
+ customFlags, userState, appInfo, userId, packageState);
if (pi == null) {
return null;
}
final ResolveInfo res = new ResolveInfo();
res.providerInfo = pi;
- if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+ if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
res.priority = filter.getPriority();
@@ -1959,46 +1637,46 @@
return input.second.getIntentFilter();
}
- private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
- private long mFlags;
+ final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
}
- private static final class ServiceIntentResolver
+ public static final class ServiceIntentResolver
extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> {
// Default constructor
- ServiceIntentResolver() {
+ ServiceIntentResolver(@NonNull UserManagerService userManager) {
+ super(userManager);
}
// Copy constructor used in creating snapshots
- ServiceIntentResolver(ServiceIntentResolver orig) {
- copyFrom(orig);
+ ServiceIntentResolver(@NonNull ServiceIntentResolver orig,
+ @NonNull UserManagerService userManager) {
+ super(orig, userManager);
mServices.putAll(orig.mServices);
- mFlags = orig.mFlags;
}
@Override
- public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
- boolean defaultOnly, int userId) {
- mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
- return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+ public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+ return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
}
- List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
- int userId) {
- if (!sUserManager.exists(userId)) return null;
- mFlags = flags;
- return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
- userId);
+ List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ return super.queryIntent(computer, intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
}
- List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
- long flags, List<ParsedService> packageServices, int userId) {
- if (!sUserManager.exists(userId)) return null;
+ List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, List<ParsedService> packageServices, int userId) {
+ if (!mUserManager.exists(userId)) return null;
if (packageServices == null) {
return Collections.emptyList();
}
- mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int servicesSize = packageServices.size();
ArrayList<Pair<ParsedService, ParsedIntentInfo>[]> listCut =
@@ -2016,10 +1694,11 @@
listCut.add(array);
}
}
- return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+ return super.queryIntentFromList(computer, intent, resolvedType,
+ defaultOnly, listCut, userId, flags);
}
- void addService(ParsedService s) {
+ void addService(@NonNull Computer computer, ParsedService s) {
mServices.put(s.getComponentName(), s);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " service:");
@@ -2037,7 +1716,7 @@
if (!intentFilter.debugCheck()) {
Log.w(TAG, "==> For Service " + s.getName());
}
- addFilter(Pair.create(s, intent));
+ addFilter(computer, Pair.create(s, intent));
}
}
@@ -2080,48 +1759,38 @@
}
@Override
- protected boolean isFilterStopped(Pair<ParsedService, ParsedIntentInfo> filter, int userId) {
- return ComponentResolver.isFilterStopped(filter, userId);
- }
-
- @Override
protected boolean isPackageForFilter(String packageName,
Pair<ParsedService, ParsedIntentInfo> info) {
return packageName.equals(info.first.getPackageName());
}
@Override
- protected ResolveInfo newResult(Pair<ParsedService, ParsedIntentInfo> pair, int match,
- int userId) {
- if (!sUserManager.exists(userId)) return null;
+ protected ResolveInfo newResult(@NonNull Computer computer,
+ Pair<ParsedService, ParsedIntentInfo> pair, int match, int userId,
+ long customFlags) {
+ if (!mUserManager.exists(userId)) return null;
ParsedService service = pair.first;
ParsedIntentInfo intentInfo = pair.second;
IntentFilter filter = intentInfo.getIntentFilter();
- AndroidPackage pkg = sPackageManagerInternal.getPackage(service.getPackageName());
- if (pkg == null) {
+ final PackageStateInternal packageState = computer.getPackageStateInternal(
+ service.getPackageName());
+ if (packageState == null || packageState.getPkg() == null
+ || !PackageStateUtils.isEnabledAndMatches(packageState, service, customFlags,
+ userId)) {
return null;
}
- if (!sPackageManagerInternal.isEnabledAndMatches(service, mFlags, userId)) {
- return null;
- }
-
- PackageStateInternal ps =
- sPackageManagerInternal.getPackageStateInternal(service.getPackageName());
- if (ps == null) {
- return null;
- }
- final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
- ServiceInfo si = PackageInfoUtils.generateServiceInfo(pkg, service, mFlags,
- userState, userId, ps);
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ ServiceInfo si = PackageInfoUtils.generateServiceInfo(packageState.getPkg(), service,
+ customFlags, userState, userId, packageState);
if (si == null) {
return null;
}
final boolean matchVisibleToInstantApp =
- (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
- final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+ (customFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean isInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
// throw out filters that aren't visible to ephemeral apps
if (matchVisibleToInstantApp
&& !(filter.isVisibleToInstantApp() || userState.isInstantApp())) {
@@ -2133,12 +1802,12 @@
}
// throw out instant app filters if updates are available; will trigger
// instant app resolution
- if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+ if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
return null;
}
final ResolveInfo res = new ResolveInfo();
res.serviceInfo = si;
- if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+ if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
res.priority = filter.getPriority();
@@ -2203,11 +1872,10 @@
}
// Keys are String (activity class name), values are Activity.
- private final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
- private long mFlags;
+ final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
}
- static final class InstantAppIntentResolver
+ public static final class InstantAppIntentResolver
extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter,
AuxiliaryResolveInfo.AuxiliaryFilter> {
/**
@@ -2224,6 +1892,13 @@
final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult =
new ArrayMap<>();
+ @NonNull
+ private final UserManagerService mUserManager;
+
+ public InstantAppIntentResolver(@NonNull UserManagerService userManager) {
+ mUserManager = userManager;
+ }
+
@Override
protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) {
return new AuxiliaryResolveInfo.AuxiliaryFilter[size];
@@ -2236,9 +1911,10 @@
}
@Override
- protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(
- AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) {
- if (!sUserManager.exists(userId)) {
+ protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(@NonNull Computer computer,
+ AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId,
+ long customFlags) {
+ if (!mUserManager.exists(userId)) {
return null;
}
final String packageName = responseObj.resolveInfo.getPackageName();
@@ -2296,40 +1972,17 @@
}
}
- private static boolean isFilterStopped(Pair<? extends ParsedComponent, ParsedIntentInfo> pair,
- int userId) {
- if (!sUserManager.exists(userId)) {
- return true;
- }
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(pair.first.getPackageName());
- if (pkg == null) {
- return false;
- }
-
- PackageStateInternal ps =
- sPackageManagerInternal.getPackageStateInternal(pair.first.getPackageName());
- if (ps == null) {
- return false;
- }
-
- // System apps are never considered stopped for purposes of
- // filtering, because there may be no way for the user to
- // actually re-launch them.
- return !ps.isSystem() && ps.getUserStateOrDefault(userId).isStopped();
- }
-
/**
* Removes MIME type from the group, by delegating to IntentResolvers
* @return true if any intent filters were changed due to this update
*/
- boolean updateMimeGroup(String packageName, String group) {
+ public boolean updateMimeGroup(@NonNull Computer computer, String packageName, String group) {
boolean hasChanges = false;
synchronized (mLock) {
- hasChanges |= mActivities.updateMimeGroup(packageName, group);
- hasChanges |= mProviders.updateMimeGroup(packageName, group);
- hasChanges |= mReceivers.updateMimeGroup(packageName, group);
- hasChanges |= mServices.updateMimeGroup(packageName, group);
+ hasChanges |= mActivities.updateMimeGroup(computer, packageName, group);
+ hasChanges |= mProviders.updateMimeGroup(computer, packageName, group);
+ hasChanges |= mReceivers.updateMimeGroup(computer, packageName, group);
+ hasChanges |= mServices.updateMimeGroup(computer, packageName, group);
if (hasChanges) {
onChanged();
}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
new file mode 100644
index 0000000..b6f2b2a0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.Computer;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
+
+public interface ComponentResolverApi {
+
+ boolean isActivityDefined(@NonNull ComponentName component);
+
+ @Nullable
+ ParsedActivity getActivity(@NonNull ComponentName component);
+
+ @Nullable
+ ParsedProvider getProvider(@NonNull ComponentName component);
+
+ @Nullable
+ ParsedActivity getReceiver(@NonNull ComponentName component);
+
+ @Nullable
+ ParsedService getService(@NonNull ComponentName component);
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ boolean componentExists(@NonNull ComponentName componentName);
+
+ @Nullable
+ List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+ @Nullable
+ List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+ @UserIdInt int userId);
+
+ @Nullable
+ ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority, long flags,
+ @UserIdInt int userId);
+
+ @Nullable
+ List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+ @Nullable
+ List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+ @UserIdInt int userId);
+
+ @Nullable
+ List<ProviderInfo> queryProviders(@NonNull Computer computer, @Nullable String processName,
+ @Nullable String metaDataKey, int uid, long flags, @UserIdInt int userId);
+
+ @Nullable
+ List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+ @Nullable
+ List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+ @UserIdInt int userId);
+
+ @Nullable
+ List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+ @Nullable
+ List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+ @UserIdInt int userId);
+
+ void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+ @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId);
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
new file mode 100644
index 0000000..6b50fc6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import com.android.server.pm.Computer;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.utils.WatchableImpl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ComponentResolverBase extends WatchableImpl implements ComponentResolverApi {
+
+ @NonNull
+ protected ComponentResolver.ActivityIntentResolver mActivities;
+
+ @NonNull
+ protected ComponentResolver.ProviderIntentResolver mProviders;
+
+ @NonNull
+ protected ComponentResolver.ReceiverIntentResolver mReceivers;
+
+ @NonNull
+ protected ComponentResolver.ServiceIntentResolver mServices;
+
+ /** Mapping from provider authority [first directory in content URI codePath) to provider. */
+ @NonNull
+ protected ArrayMap<String, ParsedProvider> mProvidersByAuthority;
+
+ @NonNull
+ protected UserManagerService mUserManager;
+
+ protected ComponentResolverBase(@NonNull UserManagerService userManager) {
+ mUserManager = userManager;
+ }
+
+ @Override
+ public boolean componentExists(@NonNull ComponentName componentName) {
+ ParsedMainComponent component = mActivities.mActivities.get(componentName);
+ if (component != null) {
+ return true;
+ }
+ component = mReceivers.mActivities.get(componentName);
+ if (component != null) {
+ return true;
+ }
+ component = mServices.mServices.get(componentName);
+ if (component != null) {
+ return true;
+ }
+ return mProviders.mProviders.get(componentName) != null;
+ }
+
+ @Nullable
+ @Override
+ public ParsedActivity getActivity(@NonNull ComponentName component) {
+ return mActivities.mActivities.get(component);
+ }
+
+ @Nullable
+ @Override
+ public ParsedProvider getProvider(@NonNull ComponentName component) {
+ return mProviders.mProviders.get(component);
+ }
+
+ @Nullable
+ @Override
+ public ParsedActivity getReceiver(@NonNull ComponentName component) {
+ return mReceivers.mActivities.get(component);
+ }
+
+ @Nullable
+ @Override
+ public ParsedService getService(@NonNull ComponentName component) {
+ return mServices.mServices.get(component);
+ }
+
+ /**
+ * Returns {@code true} if the given activity is defined by some package
+ */
+ @Override
+ public boolean isActivityDefined(@NonNull ComponentName component) {
+ return mActivities.mActivities.get(component) != null;
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, int userId) {
+ return mActivities.queryIntent(computer, intent, resolvedType, flags, userId);
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+ int userId) {
+ return mActivities.queryIntentForPackage(computer, intent, resolvedType, flags, activities,
+ userId);
+ }
+
+ @Nullable
+ @Override
+ public ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority,
+ long flags, int userId) {
+ final ParsedProvider p = mProvidersByAuthority.get(authority);
+ if (p == null) {
+ return null;
+ }
+ PackageStateInternal packageState = computer.getPackageStateInternal(p.getPackageName());
+ if (packageState == null) {
+ return null;
+ }
+ final AndroidPackage pkg = packageState.getPkg();
+ if (pkg == null) {
+ return null;
+ }
+ final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId);
+ ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, packageState);
+ if (appInfo == null) {
+ return null;
+ }
+ return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId,
+ packageState);
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, int userId) {
+ return mProviders.queryIntent(computer, intent, resolvedType, flags, userId);
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+ @UserIdInt int userId) {
+ return mProviders.queryIntentForPackage(computer, intent, resolvedType, flags, providers,
+ userId);
+ }
+
+ @Nullable
+ @Override
+ public List<ProviderInfo> queryProviders(@NonNull Computer computer,
+ @Nullable String processName, @Nullable String metaDataKey, int uid, long flags,
+ int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ List<ProviderInfo> providerList = null;
+ PackageInfoUtils.CachedApplicationInfoGenerator appInfoGenerator = null;
+ for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
+ final ParsedProvider p = mProviders.mProviders.valueAt(i);
+ if (p.getAuthority() == null) {
+ continue;
+ }
+
+ final PackageStateInternal ps = computer.getPackageStateInternal(p.getPackageName());
+ if (ps == null) {
+ continue;
+ }
+
+ AndroidPackage pkg = ps.getPkg();
+ if (pkg == null) {
+ continue;
+ }
+
+ if (processName != null && (!p.getProcessName().equals(processName)
+ || !UserHandle.isSameApp(pkg.getUid(), uid))) {
+ continue;
+ }
+ // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
+ if (metaDataKey != null && !p.getMetaData().containsKey(metaDataKey)) {
+ continue;
+ }
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new PackageInfoUtils.CachedApplicationInfoGenerator();
+ }
+ final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
+
+ final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+ pkg, p, flags, state, appInfo, userId, ps);
+ if (info == null) {
+ continue;
+ }
+ if (providerList == null) {
+ providerList = new ArrayList<>(i + 1);
+ }
+ providerList.add(info);
+ }
+ return providerList;
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, int userId) {
+ return mReceivers.queryIntent(computer, intent, resolvedType, flags, userId);
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+ @UserIdInt int userId) {
+ return mReceivers.queryIntentForPackage(computer, intent, resolvedType, flags, receivers,
+ userId);
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+ return mServices.queryIntent(computer, intent, resolvedType, flags, userId);
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+ @UserIdInt int userId) {
+ return mServices.queryIntentForPackage(computer, intent, resolvedType, flags, services,
+ userId);
+ }
+
+ @Override
+ public void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+ @NonNull List<ProviderInfo> outInfo, boolean safeMode, int userId) {
+ PackageInfoUtils.CachedApplicationInfoGenerator appInfoGenerator = null;
+ for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
+ final ParsedProvider p = mProvidersByAuthority.valueAt(i);
+ if (!p.isSyncable()) {
+ continue;
+ }
+
+ final PackageStateInternal ps = computer.getPackageStateInternal(p.getPackageName());
+ if (ps == null) {
+ continue;
+ }
+
+ final AndroidPackage pkg = ps.getPkg();
+ if (pkg == null) {
+ continue;
+ }
+
+ if (safeMode && !pkg.isSystem()) {
+ continue;
+ }
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new PackageInfoUtils.CachedApplicationInfoGenerator();
+ }
+ final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, 0, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
+
+ final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+ pkg, p, 0, state, appInfo, userId, ps);
+ if (info == null) {
+ continue;
+ }
+ outNames.add(mProvidersByAuthority.keyAt(i));
+ outInfo.add(info);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
new file mode 100644
index 0000000..ecc53eb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+
+import com.android.server.pm.Computer;
+import com.android.server.pm.PackageManagerTracedLock;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
+
+public abstract class ComponentResolverLocked extends ComponentResolverBase {
+
+ protected final PackageManagerTracedLock mLock = new PackageManagerTracedLock();
+
+ protected ComponentResolverLocked(@NonNull UserManagerService userManager) {
+ super(userManager);
+ }
+
+ @Override
+ public boolean componentExists(@NonNull ComponentName componentName) {
+ synchronized (mLock) {
+ return super.componentExists(componentName);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ParsedActivity getActivity(@NonNull ComponentName component) {
+ synchronized (mLock) {
+ return super.getActivity(component);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ParsedProvider getProvider(@NonNull ComponentName component) {
+ synchronized (mLock) {
+ return super.getProvider(component);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ParsedActivity getReceiver(@NonNull ComponentName component) {
+ synchronized (mLock) {
+ return super.getReceiver(component);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ParsedService getService(@NonNull ComponentName component) {
+ synchronized (mLock) {
+ return super.getService(component);
+ }
+ }
+
+ @Override
+ public boolean isActivityDefined(@NonNull ComponentName component) {
+ synchronized (mLock) {
+ return super.isActivityDefined(component);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryActivities(computer, intent, resolvedType, flags, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryActivities(computer, intent, resolvedType, flags, activities, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority,
+ long flags, @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryProvider(computer, authority, flags, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryProviders(computer, intent, resolvedType, flags, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryProviders(computer, intent, resolvedType, flags, providers, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ProviderInfo> queryProviders(@NonNull Computer computer,
+ @Nullable String processName, @Nullable String metaDataKey, int uid, long flags,
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryProviders(computer, processName, metaDataKey, uid, flags, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryReceivers(computer, intent, resolvedType, flags, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryReceivers(computer, intent, resolvedType, flags, receivers, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryServices(computer, intent, resolvedType, flags, userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+ @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+ @UserIdInt int userId) {
+ synchronized (mLock) {
+ return super.queryServices(computer, intent, resolvedType, flags, services, userId);
+ }
+ }
+
+ @Override
+ public void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+ @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId) {
+ synchronized (mLock) {
+ super.querySyncProviders(computer, outNames, outInfo, safeMode, userId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java
new file mode 100644
index 0000000..3a96fe6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.UserNeedsBadgingCache;
+
+public class ComponentResolverSnapshot extends ComponentResolverBase {
+
+ public ComponentResolverSnapshot(@NonNull ComponentResolver orig,
+ @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+ super(UserManagerService.getInstance());
+ mActivities = new ComponentResolver.ActivityIntentResolver(orig.mActivities, mUserManager,
+ userNeedsBadgingCache);
+ mProviders = new ComponentResolver.ProviderIntentResolver(orig.mProviders, mUserManager);
+ mReceivers = new ComponentResolver.ReceiverIntentResolver(orig.mReceivers, mUserManager,
+ userNeedsBadgingCache);
+ mServices = new ComponentResolver.ServiceIntentResolver(orig.mServices, mUserManager);
+ mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java b/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java
new file mode 100644
index 0000000..b091445
--- /dev/null
+++ b/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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.snapshot;
+
+public interface PackageDataSnapshot {
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8a105dd..bd58472 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1177,7 +1177,10 @@
@Override
public void onBootPhase(int phase) {
synchronized (mLock) {
- if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ systemReady();
+
+ } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
incrementBootCount();
} else if (phase == PHASE_BOOT_COMPLETED) {
@@ -1203,7 +1206,7 @@
}
}
- public void systemReady() {
+ private void systemReady() {
synchronized (mLock) {
mSystemReady = true;
mDreamManager = getLocalService(DreamManagerInternal.class);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index ffcb2bd..b4613a7 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -276,7 +276,7 @@
}
/**
- * Called by {@link PowerManagerService#systemReady}, *with no lock held.*
+ * Called by {@link PowerManagerService#onBootPhase}, *with no lock held.*
*/
public void systemReady() {
ConcurrentUtils.wtfIfLockHeld(TAG, mLock);
diff --git a/services/core/java/com/android/server/sensorprivacy/PersistedState.java b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
index 06f5fc0..e79efdb8 100644
--- a/services/core/java/com/android/server/sensorprivacy/PersistedState.java
+++ b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
@@ -296,7 +296,7 @@
SensorState sensorState = states.valueAt(i);
// Do not persist hardware toggle states. Will be restored on reboot
- if (userSensor.mType != SensorPrivacyManager.ToggleTypes.SOFTWARE) {
+ if (userSensor.mType != SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE) {
continue;
}
@@ -478,7 +478,7 @@
for (int j = 0; j < numSensors; j++) {
int sensor = userIndividualEnabled.keyAt(j);
SensorState sensorState = userIndividualEnabled.valueAt(j);
- result.addState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+ result.addState(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
userId, sensor, sensorState.getState(), sensorState.getLastChange());
}
}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 358f69e..a8e2d43 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -39,8 +39,8 @@
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
import static android.hardware.SensorPrivacyManager.Sources.SHELL;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
import static android.os.UserHandle.USER_NULL;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
@@ -310,11 +310,12 @@
// Reset sensor privacy when restriction is added
if (!prevRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)
&& newRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)) {
- setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, CAMERA, false);
+ setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, CAMERA, false);
}
if (!prevRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)
&& newRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)) {
- setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, MICROPHONE, false);
+ setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, MICROPHONE,
+ false);
}
}
@@ -565,7 +566,7 @@
*/
private String getSensorUseActivityName(ArraySet<Integer> sensors) {
for (Integer sensor : sensors) {
- if (isToggleSensorPrivacyEnabled(HARDWARE, sensor)) {
+ if (isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor)) {
return mContext.getResources().getString(
R.string.config_sensorUseStartedActivity_hwToggle);
}
@@ -691,7 +692,7 @@
return;
}
- setToggleSensorPrivacyUnchecked(SOFTWARE, userId, source, sensor, enable);
+ setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, enable);
}
private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source,
@@ -866,8 +867,8 @@
@Override
public boolean isCombinedToggleSensorPrivacyEnabled(int sensor) {
- return isToggleSensorPrivacyEnabled(SOFTWARE, sensor) || isToggleSensorPrivacyEnabled(
- HARDWARE, sensor);
+ return isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor)
+ || isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor);
}
private boolean isToggleSensorPrivacyEnabledInternal(int userId, int toggleType,
@@ -879,7 +880,7 @@
@Override
public boolean supportsSensorToggle(int toggleType, int sensor) {
- if (toggleType == SOFTWARE) {
+ if (toggleType == TOGGLE_TYPE_SOFTWARE) {
if (sensor == MICROPHONE) {
return mContext.getResources()
.getBoolean(R.bool.config_supportsMicToggle);
@@ -887,7 +888,7 @@
return mContext.getResources()
.getBoolean(R.bool.config_supportsCamToggle);
}
- } else if (toggleType == SensorPrivacyManager.ToggleTypes.HARDWARE) {
+ } else if (toggleType == TOGGLE_TYPE_HARDWARE) {
if (sensor == MICROPHONE) {
return mContext.getResources()
.getBoolean(R.bool.config_supportsHardwareMicToggle);
@@ -1003,37 +1004,41 @@
final int hwToggleIdx = 1;
// Get SW toggles state
mSensorPrivacyStateController.atomic(() -> {
- prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
- MICROPHONE);
- prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
- CAMERA);
- micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE,
- MICROPHONE);
- camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE, CAMERA);
+ prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+ TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+ prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+ TOGGLE_TYPE_SOFTWARE, CAMERA);
+ micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+ TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+ camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+ TOGGLE_TYPE_SOFTWARE, CAMERA);
});
// Get HW toggles state
mSensorPrivacyStateController.atomic(() -> {
- prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
- MICROPHONE);
- prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
- CAMERA);
- micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE,
- MICROPHONE);
- camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE, CAMERA);
+ prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+ TOGGLE_TYPE_HARDWARE, MICROPHONE);
+ prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+ TOGGLE_TYPE_HARDWARE, CAMERA);
+ micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+ TOGGLE_TYPE_HARDWARE, MICROPHONE);
+ camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+ TOGGLE_TYPE_HARDWARE, CAMERA);
});
if (from == USER_NULL || prevMicState[swToggleIdx] != micState[swToggleIdx]
|| prevMicState[hwToggleIdx] != micState[hwToggleIdx]) {
- mHandler.handleSensorPrivacyChanged(to, SOFTWARE, MICROPHONE,
+ mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, MICROPHONE,
micState[swToggleIdx]);
- mHandler.handleSensorPrivacyChanged(to, HARDWARE, MICROPHONE,
+ mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, MICROPHONE,
micState[hwToggleIdx]);
setGlobalRestriction(MICROPHONE, micState[swToggleIdx] || micState[hwToggleIdx]);
}
if (from == USER_NULL || prevCamState[swToggleIdx] != camState[swToggleIdx]
|| prevCamState[hwToggleIdx] != camState[hwToggleIdx]) {
- mHandler.handleSensorPrivacyChanged(to, SOFTWARE, CAMERA, camState[swToggleIdx]);
- mHandler.handleSensorPrivacyChanged(to, HARDWARE, CAMERA, camState[hwToggleIdx]);
+ mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, CAMERA,
+ camState[swToggleIdx]);
+ mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, CAMERA,
+ camState[hwToggleIdx]);
setGlobalRestriction(CAMERA, camState[swToggleIdx] || camState[hwToggleIdx]);
}
}
@@ -1437,7 +1442,7 @@
public boolean isSensorPrivacyEnabled(int userId, int sensor) {
return SensorPrivacyService.this
.mSensorPrivacyServiceImpl.isToggleSensorPrivacyEnabledInternal(userId,
- SOFTWARE, sensor);
+ TOGGLE_TYPE_SOFTWARE, sensor);
}
@Override
@@ -1487,10 +1492,12 @@
userId = (userId == UserHandle.USER_CURRENT ? mCurrentUser : userId);
final int realUserId = (userId == UserHandle.USER_NULL ? mContext.getUserId() : userId);
- sps.setToggleSensorPrivacyUnchecked(HARDWARE, realUserId, OTHER, sensor, enable);
+ sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_HARDWARE, realUserId, OTHER, sensor,
+ enable);
// Also disable the SW toggle when disabling the HW toggle
if (!enable) {
- sps.setToggleSensorPrivacyUnchecked(SOFTWARE, realUserId, OTHER, sensor, enable);
+ sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, realUserId, OTHER, sensor,
+ enable);
}
}
}
@@ -1546,9 +1553,9 @@
if (!mIsInEmergencyCall) {
mIsInEmergencyCall = true;
if (mSensorPrivacyServiceImpl
- .isToggleSensorPrivacyEnabled(SOFTWARE, MICROPHONE)) {
+ .isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, MICROPHONE)) {
mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
- SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
+ TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
mMicUnmutedForEmergencyCall = true;
} else {
mMicUnmutedForEmergencyCall = false;
@@ -1574,7 +1581,7 @@
mIsInEmergencyCall = false;
if (mMicUnmutedForEmergencyCall) {
mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
- SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
+ TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
mMicUnmutedForEmergencyCall = false;
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index c638201..9bbae4b 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -29,7 +29,9 @@
import android.os.IHwBinder;
import android.os.RemoteException;
import android.system.OsConstants;
+import android.util.Log;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -54,6 +56,8 @@
* </ul>
*/
final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+ private static final String TAG = "SoundTriggerHw2Compat";
+
private final @NonNull Runnable mRebootRunnable;
private final @NonNull IHwBinder mBinder;
private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
@@ -226,6 +230,16 @@
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (hidlModel.data != null) {
+ try {
+ hidlModel.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
}
}
@@ -252,6 +266,16 @@
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } finally {
+ // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+ // clause, instead of having to explicitly close internal fields.
+ if (hidlModel.common.data != null) {
+ try {
+ hidlModel.common.data.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file", e);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d84d724..b0efa5b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2537,7 +2537,6 @@
}
removeStartingWindowAnimation(true /* prepareAnimation */);
- // TODO(b/215316431): Add tests
final Task task = getTask();
if (prevEligibleForLetterboxEducation != isEligibleForLetterboxEducation()
&& task != null) {
@@ -7717,7 +7716,6 @@
* once the starting window is removed in {@link #removeStartingWindow}).
* </ul>
*/
- // TODO(b/215316431): Add tests
boolean isEligibleForLetterboxEducation() {
return mWmService.mLetterboxConfiguration.getIsEducationEnabled()
&& mIsEligibleForFixedOrientationLetterbox
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ddfdddc..4c5c705 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3326,8 +3326,8 @@
}
if (mInsetsStateController != null) {
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
- final InsetsSourceProvider provider = mInsetsStateController.peekSourceProvider(
- type);
+ final WindowContainerInsetsSourceProvider provider = mInsetsStateController
+ .peekSourceProvider(type);
if (provider != null) {
provider.dumpDebug(proto, type == ITYPE_IME ? IME_INSETS_SOURCE_PROVIDER :
INSETS_SOURCE_PROVIDERS, logLevel);
@@ -4036,7 +4036,7 @@
// app.
assignWindowLayers(true /* setLayoutNeeded */);
// 3. The z-order of IME might have been changed. Update the above insets state.
- mInsetsStateController.updateAboveInsetsState(mInputMethodWindow,
+ mInsetsStateController.updateAboveInsetsState(
mInsetsStateController.getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
// 4. Update the IME control target to apply any inset change and animation.
// 5. Reparent the IME container surface to either the input target app, or the IME window
@@ -4167,7 +4167,7 @@
if (mImeInputTarget != target) {
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
setImeInputTarget(target);
- mInsetsStateController.updateAboveInsetsState(mInputMethodWindow, mInsetsStateController
+ mInsetsStateController.updateAboveInsetsState(mInsetsStateController
.getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
updateImeControlTarget();
}
@@ -4810,6 +4810,17 @@
}
@Override
+ void updateAboveInsetsState(InsetsState aboveInsetsState,
+ SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+ ArraySet<WindowState> insetsChangedWindows) {
+ if (skipImeWindowsDuringTraversal(mDisplayContent)) {
+ return;
+ }
+ super.updateAboveInsetsState(aboveInsetsState, localInsetsSourceProvidersFromParent,
+ insetsChangedWindows);
+ }
+
+ @Override
boolean forAllWindows(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
final DisplayContent dc = mDisplayContent;
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 8f97220..f24e429 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -46,7 +46,7 @@
* Controller for IME inset source on the server. It's called provider as it provides the
* {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
*/
-final class ImeInsetsSourceProvider extends InsetsSourceProvider {
+final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider {
private InsetsControlTarget mImeRequester;
private Runnable mShowImeRunner;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 433f1071..bb6d83c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -176,7 +176,8 @@
}
boolean isHidden(@InternalInsetsType int type) {
- final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
+ final WindowContainerInsetsSourceProvider provider = mStateController
+ .peekSourceProvider(type);
return provider != null && provider.hasWindowContainer()
&& !provider.getSource().isVisible();
}
@@ -358,10 +359,10 @@
// IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
if (type == ITYPE_IME) {
- ArrayMap<Integer, InsetsSourceProvider> providers = mStateController
+ ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
.getSourceProviders();
for (int i = providers.size() - 1; i >= 0; i--) {
- InsetsSourceProvider otherProvider = providers.valueAt(i);
+ WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
if (otherProvider.overridesImeFrame()) {
InsetsSource override =
new InsetsSource(
@@ -662,7 +663,7 @@
final IntArray showingTransientTypes = mShowingTransientTypes;
for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
final @InternalInsetsType int type = showingTransientTypes.get(i);
- InsetsSourceProvider provider = mStateController.getSourceProvider(type);
+ WindowContainerInsetsSourceProvider provider = mStateController.getSourceProvider(type);
InsetsSourceControl control = provider.getControl(mDummyControlTarget);
if (control == null || control.getLeash() == null) {
continue;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 4c7a297..047bf2f 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -65,7 +65,7 @@
* Controller for a specific inset source on the server. It's called provider as it provides the
* {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
*/
-class InsetsSourceProvider {
+abstract class InsetsSourceProvider {
protected final DisplayContent mDisplayContent;
protected final @NonNull InsetsSource mSource;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 32e70d9..78608e2 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -35,7 +35,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
-import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
@@ -56,7 +55,8 @@
private final InsetsState mState = new InsetsState();
private final DisplayContent mDisplayContent;
- private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>();
+ private final ArrayMap<Integer, WindowContainerInsetsSourceProvider> mProviders =
+ new ArrayMap<>();
private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
new ArrayMap<>();
private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
@@ -108,21 +108,22 @@
return result;
}
- ArrayMap<Integer, InsetsSourceProvider> getSourceProviders() {
+ ArrayMap<Integer, WindowContainerInsetsSourceProvider> getSourceProviders() {
return mProviders;
}
/**
* @return The provider of a specific type.
*/
- InsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
+ WindowContainerInsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
if (type == ITYPE_IME) {
return mProviders.computeIfAbsent(type,
key -> new ImeInsetsSourceProvider(
mState.getSource(key), this, mDisplayContent));
} else {
return mProviders.computeIfAbsent(type,
- key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+ key -> new WindowContainerInsetsSourceProvider(mState.getSource(key), this,
+ mDisplayContent));
}
}
@@ -133,7 +134,8 @@
/**
* @return The provider of a specific type or null if we don't have it.
*/
- @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) {
+ @Nullable
+ WindowContainerInsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) {
return mProviders.get(type);
}
@@ -153,58 +155,25 @@
}
/**
- * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the
- * z-order of a window is changed.
+ * Updates {@link WindowState#mAboveInsetsState} for all windows in the display.
*
- * @param win The window whose z-order has changed.
* @param notifyInsetsChange {@code true} if the clients should be notified about the change.
*/
- void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) {
- if (win == null || win.getDisplayContent() != mDisplayContent) {
- return;
- }
- final boolean[] aboveWin = { true };
+ void updateAboveInsetsState(boolean notifyInsetsChange) {
final InsetsState aboveInsetsState = new InsetsState();
aboveInsetsState.set(mState,
displayCutout() | systemGestures() | mandatorySystemGestures());
- final SparseArray<InsetsSource> winProvidedSources = win.getProvidedInsetsSources();
- final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
- mDisplayContent.forAllWindows(w -> {
- if (aboveWin[0]) {
- if (w == win) {
- aboveWin[0] = false;
- if (!win.mAboveInsetsState.equals(aboveInsetsState)) {
- win.mAboveInsetsState.set(aboveInsetsState);
- insetsChangedWindows.add(win);
- }
- return winProvidedSources.size() == 0;
- } else {
- final SparseArray<InsetsSource> providedSources = w.getProvidedInsetsSources();
- for (int i = providedSources.size() - 1; i >= 0; i--) {
- aboveInsetsState.addSource(providedSources.valueAt(i));
- }
- if (winProvidedSources.size() == 0) {
- return false;
- }
- boolean changed = false;
- for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
- changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i));
- }
- if (changed) {
- insetsChangedWindows.add(w);
- }
- }
- } else {
- for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
- w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i));
- }
- insetsChangedWindows.add(w);
- }
- return false;
- }, true /* traverseTopToBottom */);
+ final ArraySet<WindowState> insetsChangedWindows = new ArraySet<>();
+ final SparseArray<InsetsSourceProvider>
+ localInsetsSourceProvidersFromParent = new SparseArray<>();
+ // This method will iterate on the entire hierarchy in top to bottom z-order manner. The
+ // aboveInsetsState will be modified as per the insets provided by the WindowState being
+ // visited.
+ mDisplayContent.updateAboveInsetsState(aboveInsetsState,
+ localInsetsSourceProvidersFromParent, insetsChangedWindows);
if (notifyInsetsChange) {
for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
- mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
+ mDispatchInsetsChanged.accept(insetsChangedWindows.valueAt(i));
}
}
}
@@ -245,7 +214,7 @@
void computeSimulatedState(WindowState win, DisplayFrames displayFrames, Rect winFrame) {
final InsetsState state = displayFrames.mInsetsState;
for (int i = mProviders.size() - 1; i >= 0; i--) {
- final InsetsSourceProvider provider = mProviders.valueAt(i);
+ final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i);
if (provider.mWindowContainer == win) {
state.addSource(provider.createSimulatedSource(displayFrames, winFrame));
}
@@ -302,7 +271,7 @@
if (target == previous) {
return;
}
- final InsetsSourceProvider provider = mProviders.get(type);
+ final WindowContainerInsetsSourceProvider provider = mProviders.get(type);
if (provider == null) {
return;
}
@@ -333,7 +302,7 @@
if (fakeTarget == previous) {
return;
}
- final InsetsSourceProvider provider = mProviders.get(type);
+ final WindowContainerInsetsSourceProvider provider = mProviders.get(type);
if (provider == null) {
return;
}
@@ -388,7 +357,7 @@
}
mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
for (int i = mProviders.size() - 1; i >= 0; i--) {
- final InsetsSourceProvider provider = mProviders.valueAt(i);
+ final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i);
provider.onSurfaceTransactionApplied();
}
final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
diff --git a/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java b/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
new file mode 100644
index 0000000..6e8beee
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Rect;
+import android.util.Slog;
+import android.view.InsetsSource;
+
+/**
+ * An {@link InsetsSourceProvider} which doesn't have a backing window or a window container.
+ */
+public class RectInsetsSourceProvider extends InsetsSourceProvider {
+ private static final String TAG = TAG_WITH_CLASS_NAME
+ ? RectInsetsSourceProvider.class.getSimpleName()
+ : TAG_WM;
+
+ RectInsetsSourceProvider(InsetsSource source,
+ InsetsStateController stateController, DisplayContent displayContent) {
+ super(source, stateController, displayContent);
+ }
+
+ /**
+ * Sets the given {@code rect} as the frame of the underlying {@link InsetsSource}.
+ */
+ void setRect(Rect rect) {
+ mSource.setFrame(rect);
+ mSource.setVisible(true);
+ }
+
+ @Override
+ void onPostLayout() {
+ if (WindowManagerDebugConfig.DEBUG) {
+ Slog.d(TAG, "onPostLayout(), not calling super.onPostLayout().");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 358a615..ffa1a60 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2824,7 +2824,7 @@
TaskTransitionSpec spec = mWmService.mTaskTransitionSpec;
if (spec != null) {
for (@InsetsState.InternalInsetsType int insetType : spec.animationBoundInsets) {
- InsetsSourceProvider insetProvider = getDisplayContent()
+ WindowContainerInsetsSourceProvider insetProvider = getDisplayContent()
.getInsetsStateController()
.getSourceProvider(insetType);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 45fdc04..bbc8462 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -57,6 +57,7 @@
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -85,6 +86,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
@@ -148,12 +150,24 @@
// onParentChanged() notification.
boolean mReparenting;
- protected @Nullable InsetsSourceProvider mControllableInsetProvider;
+ /**
+ * Map of {@link InsetsState.InternalInsetsType} to the {@link InsetsSourceProvider} that
+ * provides local insets for all children of the current {@link WindowContainer}.
+ *
+ * Note that these InsetsSourceProviders are not part of the {@link InsetsStateController} and
+ * live here. These are supposed to provide insets only to the subtree of the current
+ * {@link WindowContainer}.
+ */
+ @Nullable
+ SparseArray<InsetsSourceProvider> mLocalInsetsSourceProviders = null;
+
+ @Nullable
+ protected InsetsSourceProvider mControllableInsetProvider;
/**
* The insets sources provided by this windowContainer.
*/
- private SparseArray<InsetsSource> mProvidedInsetsSources = null;
+ protected SparseArray<InsetsSource> mProvidedInsetsSources = null;
// List of children for this window container. List is in z-order as the children appear on
// screen with the top-most window container at the tail of the list.
@@ -340,14 +354,135 @@
}
/**
- * Set's an {@link InsetsSourceProvider} to be associated with this window, but only if the
- * provider itself is controllable, as one window can be the provider of more than one inset
- * type (i.e. gesture insets). If this window is controllable, all its animations must be
- * controlled by its control target, and the visibility of this window should be taken account
- * into the state of the control target.
+ * Updates the {@link WindowState#mAboveInsetsState} and
+ * {@link WindowState#mMergedLocalInsetsSources} by visiting the entire hierarchy.
+ *
+ * {@link WindowState#mAboveInsetsState} is updated by visiting all the windows in z-order
+ * top-to-bottom manner and considering the {@link WindowContainer#mProvidedInsetsSources}
+ * provided by the {@link WindowState}s at the top.
+ * {@link WindowState#updateAboveInsetsState(InsetsState, SparseArray, ArraySet)} visits the
+ * IME container in the correct order to make sure the IME insets are passed correctly to the
+ * {@link WindowState}s below it.
+ *
+ * {@link WindowState#mMergedLocalInsetsSources} is updated by considering
+ * {@link WindowContainer#mLocalInsetsSourceProviders} provided by all the parents of the
+ * window.
+ * A given insetsType can be provided as a LocalInsetsSourceProvider only once in a
+ * Parent-to-leaf path.
+ *
+ * Examples: Please take a look at
+ * {@link WindowContainerTests#testAddLocalInsetsSourceProvider()}
+ * {@link
+ * WindowContainerTests#testAddLocalInsetsSourceProvider_windowSkippedIfProvidingOnParent()}
+ * {@link WindowContainerTests#testRemoveLocalInsetsSourceProvider()}.
+ *
+ * @param aboveInsetsState The InsetsState of all the Windows above the current container.
+ * @param localInsetsSourceProvidersFromParent The local InsetsSourceProviders provided by all
+ * the parents in the hierarchy of the current
+ * container.
+ * @param insetsChangedWindows The windows which the insets changed have changed for.
+ */
+ void updateAboveInsetsState(InsetsState aboveInsetsState,
+ SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+ ArraySet<WindowState> insetsChangedWindows) {
+ SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
+ localInsetsSourceProvidersFromParent;
+ if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+ mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
+ for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
+ mergedLocalInsetsSourceProviders.put(
+ mLocalInsetsSourceProviders.keyAt(i),
+ mLocalInsetsSourceProviders.valueAt(i));
+ }
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ mChildren.get(i).updateAboveInsetsState(aboveInsetsState,
+ mergedLocalInsetsSourceProviders, insetsChangedWindows);
+ }
+ }
+
+ static <T> SparseArray<T> createShallowCopy(SparseArray<T> inputArray) {
+ SparseArray<T> copyOfInput = new SparseArray<>(inputArray.size());
+ for (int i = 0; i < inputArray.size(); i++) {
+ copyOfInput.append(inputArray.keyAt(i), inputArray.valueAt(i));
+ }
+ return copyOfInput;
+ }
+
+ /**
+ * Sets the given {@code providerFrame} as one of the insets provider for this window
+ * container. These insets are only passed to the subtree of the current WindowContainer.
+ * For a given WindowContainer-to-Leaf path, one insetsType can't be overridden more than once.
+ * If that happens, only the latest one will be chosen.
+ *
+ * @param providerFrame the frame that will act as one of the insets providers for this window
+ * container
+ * @param insetsTypes the insets type which the providerFrame provides
+ */
+ void addLocalRectInsetsSourceProvider(Rect providerFrame,
+ @InsetsState.InternalInsetsType int[] insetsTypes) {
+ if (insetsTypes == null || insetsTypes.length == 0) {
+ throw new IllegalArgumentException("Insets type not specified.");
+ }
+ if (mLocalInsetsSourceProviders == null) {
+ mLocalInsetsSourceProviders = new SparseArray<>();
+ }
+ for (int i = 0; i < insetsTypes.length; i++) {
+ InsetsSourceProvider insetsSourceProvider =
+ mLocalInsetsSourceProviders.get(insetsTypes[i]);
+ if (insetsSourceProvider != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "The local insets provider for this type " + insetsTypes[i]
+ + " already exists. Overwriting");
+ }
+ }
+ if (insetsSourceProvider == null
+ || !(insetsSourceProvider instanceof RectInsetsSourceProvider)) {
+ insetsSourceProvider =
+ new RectInsetsSourceProvider(
+ new InsetsSource(insetsTypes[i]),
+ mDisplayContent.getInsetsStateController(),
+ mDisplayContent);
+ mLocalInsetsSourceProviders.put(insetsTypes[i], insetsSourceProvider);
+ }
+ ((RectInsetsSourceProvider) insetsSourceProvider).setRect(providerFrame);
+ }
+ mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+ }
+
+ void removeLocalInsetsSourceProvider(@InsetsState.InternalInsetsType int[] insetsTypes) {
+ if (insetsTypes == null || insetsTypes.length == 0) {
+ throw new IllegalArgumentException("Insets type not specified.");
+ }
+ if (mLocalInsetsSourceProviders == null) {
+ return;
+ }
+
+ for (int i = 0; i < insetsTypes.length; i++) {
+ InsetsSourceProvider insetsSourceProvider =
+ mLocalInsetsSourceProviders.get(insetsTypes[i]);
+ if (insetsSourceProvider == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Given insets type " + insetsTypes[i] + " doesn't have a "
+ + "local insetsSourceProvider.");
+ }
+ continue;
+ }
+ mLocalInsetsSourceProviders.remove(insetsTypes[i]);
+ }
+ mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+ }
+
+ /**
+ * Sets an {@link InsetsSourceProvider} to be associated with this {@code WindowContainer},
+ * but only if the provider itself is controllable, as one window can be the provider of more
+ * than one inset type (i.e. gesture insets). If this {code WindowContainer} is controllable,
+ * all its animations must be controlled by its control target, and the visibility of this
+ * {code WindowContainer} should be taken account into the state of the control target.
*
* @param insetProvider the provider which should not be visible to the client.
- * @see #getInsetsState()
+ * @see WindowState#getInsetsState()
*/
void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
mControllableInsetProvider = insetProvider;
@@ -3682,8 +3817,8 @@
new ArrayList<>(insetTypes.size());
for (int insetType : insetTypes) {
- InsetsSourceProvider insetProvider = getDisplayContent().getInsetsStateController()
- .getSourceProvider(insetType);
+ WindowContainerInsetsSourceProvider insetProvider = getDisplayContent()
+ .getInsetsStateController().getSourceProvider(insetType);
// Will apply it immediately to current leash and to all future inset animations
// until we disable it.
diff --git a/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java b/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
new file mode 100644
index 0000000..aa2e8f5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.server.wm;
+
+import android.view.InsetsSource;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
+ */
+class WindowContainerInsetsSourceProvider extends InsetsSourceProvider {
+ // TODO(b/218734524): Move the window container specific stuff from InsetsSourceProvider to
+ // this class.
+
+ WindowContainerInsetsSourceProvider(InsetsSource source,
+ InsetsStateController stateController, DisplayContent displayContent) {
+ super(source, stateController, displayContent);
+ }
+}
+
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4ff2b6a..5b1021e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1823,7 +1823,7 @@
// This window doesn't have a frame yet. Don't let this window cause the insets change.
displayContent.getInsetsStateController().updateAboveInsetsState(
- win, false /* notifyInsetsChanged */);
+ false /* notifyInsetsChanged */);
outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
getInsetsSourceControls(win, outActiveControls);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1ab8cbf..87ef09e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -214,9 +214,11 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -680,6 +682,11 @@
final InsetsState mAboveInsetsState = new InsetsState();
/**
+ * The insets state of sources provided by the overrides set on any parent up the hierarchy.
+ */
+ SparseArray<InsetsSource> mMergedLocalInsetsSources = null;
+
+ /**
* Surface insets from the previous call to relayout(), used to track
* if we are changing the Surface insets.
*/
@@ -1662,13 +1669,10 @@
return insetsPolicy.adjustInsetsForWindow(this, rotatedState);
}
final InsetsSourceProvider provider = getControllableInsetProvider();
- final InsetsStateController insetsStateController = getDisplayContent()
- .getInsetsStateController();
final @InternalInsetsType int insetTypeProvidedByWindow = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
- final InsetsState rawInsetsState = getFrozenInsetsState() != null
- ? getFrozenInsetsState() : (mAttrs.receiveInsetsIgnoringZOrder
- ? insetsStateController.getRawInsetsState() : mAboveInsetsState);
+ final InsetsState rawInsetsState =
+ mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
final InsetsState insetsStateForWindow = insetsPolicy
.enforceInsetsPolicyForTarget(insetTypeProvidedByWindow,
getWindowingMode(), isAlwaysOnTop(), rawInsetsState);
@@ -1676,6 +1680,21 @@
includeTransient);
}
+ private InsetsState getMergedInsetsState() {
+ final InsetsState globalInsetsState = mAttrs.receiveInsetsIgnoringZOrder
+ ? getDisplayContent().getInsetsStateController().getRawInsetsState()
+ : mAboveInsetsState;
+ if (mMergedLocalInsetsSources == null) {
+ return globalInsetsState;
+ }
+
+ final InsetsState mergedInsetsState = new InsetsState(globalInsetsState);
+ for (int i = 0; i < mMergedLocalInsetsSources.size(); i++) {
+ mergedInsetsState.addSource(mMergedLocalInsetsSources.valueAt(i));
+ }
+ return mergedInsetsState;
+ }
+
/**
* Returns the insets state for the client and scales the frames if the client is in the size
* compatible mode.
@@ -4725,6 +4744,58 @@
return false;
}
+ @Override
+ void updateAboveInsetsState(InsetsState aboveInsetsState,
+ SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+ ArraySet<WindowState> insetsChangedWindows) {
+ SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
+ localInsetsSourceProvidersFromParent;
+ if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+ mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
+ for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
+ mergedLocalInsetsSourceProviders.put(
+ mLocalInsetsSourceProviders.keyAt(i),
+ mLocalInsetsSourceProviders.valueAt(i));
+ }
+ }
+ final SparseArray<InsetsSource> mergedLocalInsetsSourcesFromParent =
+ toInsetsSources(mergedLocalInsetsSourceProviders);
+
+ // Insets provided by the IME window can effect all the windows below it and hence it needs
+ // to be visited in the correct order. Because of which updateAboveInsetsState() can't be
+ // used here and instead forAllWindows() is used.
+ forAllWindows(w -> {
+ if (!w.mAboveInsetsState.equals(aboveInsetsState)) {
+ w.mAboveInsetsState.set(aboveInsetsState);
+ insetsChangedWindows.add(w);
+ }
+
+ if (!mergedLocalInsetsSourcesFromParent.contentEquals(w.mMergedLocalInsetsSources)) {
+ w.mMergedLocalInsetsSources = createShallowCopy(
+ mergedLocalInsetsSourcesFromParent);
+ insetsChangedWindows.add(w);
+ }
+
+ final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
+ if (providedSources != null) {
+ for (int i = providedSources.size() - 1; i >= 0; i--) {
+ aboveInsetsState.addSource(providedSources.valueAt(i));
+ }
+ }
+ }, true /* traverseTopToBottom */);
+ }
+
+ private static SparseArray<InsetsSource> toInsetsSources(
+ SparseArray<InsetsSourceProvider> insetsSourceProviders) {
+ final SparseArray<InsetsSource> insetsSources = new SparseArray<>(
+ insetsSourceProviders.size());
+ for (int i = 0; i < insetsSourceProviders.size(); i++) {
+ insetsSources.append(insetsSourceProviders.keyAt(i),
+ insetsSourceProviders.valueAt(i).getSource());
+ }
+ return insetsSources;
+ }
+
private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
// We want to consume the positive sublayer children first because they need to appear
// above the parent, then this window (the parent), and then the negative sublayer children
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2e801ab..e895d37 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2707,15 +2707,6 @@
systemTheme.rebase();
}
- t.traceBegin("MakePowerManagerServiceReady");
- try {
- // TODO: use boot phase
- mPowerManagerService.systemReady();
- } catch (Throwable e) {
- reportWtf("making Power Manager Service ready", e);
- }
- t.traceEnd();
-
// Permission policy service
t.traceBegin("StartPermissionPolicyService");
mSystemServiceManager.startService(PermissionPolicyService.class);
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f72b23c..bcdbc5d 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -63,6 +63,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
@@ -128,6 +129,19 @@
private final PackageManager mPackageManager;
+ private static final String MIDI_LEGACY_STRING = "MIDI 1.0";
+ private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0";
+
+ // Used to lock mUsbMidiLegacyDeviceOpenCount and mUsbMidiUniversalDeviceInUse.
+ private final Object mUsbMidiLock = new Object();
+
+ // Number of times a USB MIDI 1.0 device has opened, based on the device name.
+ private final HashMap<String, Integer> mUsbMidiLegacyDeviceOpenCount =
+ new HashMap<String, Integer>();
+
+ // Whether a USB MIDI device has opened, based on the device name.
+ private final HashSet<String> mUsbMidiUniversalDeviceInUse = new HashSet<String>();
+
// UID of BluetoothMidiService
private int mBluetoothServiceUid;
@@ -271,6 +285,12 @@
}
for (DeviceConnection connection : mDeviceConnections.values()) {
+ if (connection.getDevice().getDeviceInfo().getType()
+ == MidiDeviceInfo.TYPE_USB) {
+ synchronized (mUsbMidiLock) {
+ removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
+ }
+ }
connection.getDevice().removeDeviceConnection(connection);
}
}
@@ -784,6 +804,15 @@
}
}
+ if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) {
+ synchronized (mUsbMidiLock) {
+ if (isUsbMidiDeviceInUseLocked(deviceInfo)) {
+ throw new IllegalArgumentException("device already in use: " + deviceInfo);
+ }
+ addUsbMidiDeviceLocked(deviceInfo);
+ }
+ }
+
// clear calling identity so bindService does not fail
final long identity = Binder.clearCallingIdentity();
try {
@@ -1216,4 +1245,82 @@
}
pw.decreaseIndent();
}
+
+ // hold mUsbMidiLock before calling this
+ private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
+ String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+ if (name.length() < MIDI_LEGACY_STRING.length()) {
+ return false;
+ }
+ String deviceName = extractUsbDeviceName(name);
+ String tagName = extractUsbDeviceTag(name);
+
+ // Only one MIDI 2.0 device can be used at once.
+ // Multiple MIDI 1.0 devices can be used at once.
+ if (mUsbMidiUniversalDeviceInUse.contains(deviceName)
+ || ((tagName).equals(MIDI_UNIVERSAL_STRING)
+ && (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)))) {
+ return true;
+ }
+ return false;
+ }
+
+ // hold mUsbMidiLock before calling this
+ void addUsbMidiDeviceLocked(MidiDeviceInfo info) {
+ String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+ if (name.length() < MIDI_LEGACY_STRING.length()) {
+ return;
+ }
+ String deviceName = extractUsbDeviceName(name);
+ String tagName = extractUsbDeviceTag(name);
+
+ if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+ mUsbMidiUniversalDeviceInUse.add(deviceName);
+ } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+ int count = mUsbMidiLegacyDeviceOpenCount.getOrDefault(deviceName, 0) + 1;
+ mUsbMidiLegacyDeviceOpenCount.put(deviceName, count);
+ }
+ }
+
+ // hold mUsbMidiLock before calling this
+ void removeUsbMidiDeviceLocked(MidiDeviceInfo info) {
+ String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+ if (name.length() < MIDI_LEGACY_STRING.length()) {
+ return;
+ }
+ String deviceName = extractUsbDeviceName(name);
+ String tagName = extractUsbDeviceTag(name);
+
+ if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+ mUsbMidiUniversalDeviceInUse.remove(deviceName);
+ } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+ if (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)) {
+ int count = mUsbMidiLegacyDeviceOpenCount.get(deviceName);
+ if (count > 1) {
+ mUsbMidiLegacyDeviceOpenCount.put(deviceName, count - 1);
+ } else {
+ mUsbMidiLegacyDeviceOpenCount.remove(deviceName);
+ }
+ }
+ }
+ }
+
+ // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+ // This is defined in UsbDirectMidiDevice.java.
+ // This function extracts out the "manufacturer product#Id " part.
+ // Two devices would have the same device name if they had the following property name:
+ // "manufacturer product#Id MIDI 1.0"
+ // "manufacturer product#Id MIDI 2.0"
+ // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+ String extractUsbDeviceName(String propertyName) {
+ return propertyName.substring(0, propertyName.length() - MIDI_LEGACY_STRING.length());
+ }
+
+ // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+ // This is defined in UsbDirectMidiDevice.java.
+ // This function extracts the "MIDI 1.0" part.
+ // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+ String extractUsbDeviceTag(String propertyName) {
+ return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length());
+ }
}
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index c5709fc..43b2e1e 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -29,6 +29,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.server.pm.resolution.ComponentResolver
import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
import com.android.server.testutils.TestHandler
import com.android.server.testutils.mock
@@ -46,6 +47,8 @@
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.intThat
import org.mockito.Mockito.never
import org.mockito.Mockito.same
@@ -337,8 +340,8 @@
val mockSettings = Settings(mockedPkgSettings)
val mockComponentResolver: ComponentResolver = mockThrowOnUnmocked {
params.componentName?.let {
- whenever(this.componentExists(same(it))) { mockActivity != null }
- whenever(this.getActivity(same(it))) { mockActivity }
+ doReturn(mockActivity != null).`when`(this).componentExists(same(it))
+ doReturn(mockActivity).`when`(this).getActivity(same(it))
}
whenever(this.snapshot()) { this@mockThrowOnUnmocked }
whenever(registerObserver(any())).thenCallRealMethod()
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 4ec1641..ca5bf20 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -68,6 +68,7 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.parsing.ParsingPackage
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
+import com.android.server.pm.resolution.ComponentResolver
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.supplementalprocess.SupplementalProcessManagerLocal
import com.android.server.testutils.TestHandler
@@ -632,7 +633,7 @@
}
private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) {
- whenever(mocks.componentResolver.queryActivities(
+ whenever(mocks.componentResolver.queryActivities(any(),
argThat { intent: Intent? -> intent != null && (action == intent.action) },
nullable(), anyLong(), anyInt())) {
ArrayList(activities.asList().map { info: ActivityInfo? ->
@@ -642,7 +643,7 @@
}
private fun mockQueryServices(action: String, vararg services: ServiceInfo) {
- whenever(mocks.componentResolver.queryServices(
+ whenever(mocks.componentResolver.queryServices(any(),
argThat { intent: Intent? -> intent != null && (action == intent.action) },
nullable(), anyLong(), anyInt())) {
ArrayList(services.asList().map { info ->
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
index 0411b94..9cf6c03 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -263,7 +263,7 @@
@Test
public void testUserActivityOnDeviceStateChange() {
createService();
- mService.systemReady();
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
final DisplayInfo info = new DisplayInfo();
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index 64e8613..8b7cc74 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -18,8 +18,8 @@
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -102,106 +102,106 @@
public void testMigration1() throws IOException {
PersistedState ps = migrateFromFile(PERSISTENCE_FILE1);
- assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
- assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+ assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
- assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
- assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 0, CAMERA));
- assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
}
@Test
public void testMigration2() throws IOException {
PersistedState ps = migrateFromFile(PERSISTENCE_FILE2);
- assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
- assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+ assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
- assertTrue(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
- assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+ assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+ assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled());
- assertNull(ps.getState(SOFTWARE, 11, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 11, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, CAMERA));
- assertTrue(ps.getState(SOFTWARE, 12, MICROPHONE).isEnabled());
- assertNull(ps.getState(SOFTWARE, 12, CAMERA));
+ assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, MICROPHONE).isEnabled());
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, CAMERA));
- assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 0, CAMERA));
- assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 10, CAMERA));
- assertNull(ps.getState(HARDWARE, 11, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 11, CAMERA));
- assertNull(ps.getState(HARDWARE, 12, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 12, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, CAMERA));
}
@Test
public void testMigration3() throws IOException {
PersistedState ps = migrateFromFile(PERSISTENCE_FILE3);
- assertFalse(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
- assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+ assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
- assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
- assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 0, CAMERA));
- assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
}
@Test
public void testMigration4() throws IOException {
PersistedState ps = migrateFromFile(PERSISTENCE_FILE4);
- assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
- assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+ assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+ assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
- assertFalse(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
- assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+ assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
- assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 0, CAMERA));
- assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
}
@Test
public void testMigration5() throws IOException {
PersistedState ps = migrateFromFile(PERSISTENCE_FILE5);
- assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
- assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+ assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
- assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
- assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+ assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled());
- assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 0, CAMERA));
- assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
}
@Test
public void testMigration6() throws IOException {
PersistedState ps = migrateFromFile(PERSISTENCE_FILE6);
- assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
- assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
- assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 0, CAMERA));
- assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
}
private PersistedState migrateFromFile(String fileName) throws IOException {
@@ -233,32 +233,32 @@
public void testPersistence1Version2() throws IOException {
PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE7);
- assertEquals(1, ps.getState(SOFTWARE, 0, MICROPHONE).getState());
- assertEquals(123L, ps.getState(SOFTWARE, 0, MICROPHONE).getLastChange());
- assertEquals(2, ps.getState(SOFTWARE, 0, CAMERA).getState());
- assertEquals(123L, ps.getState(SOFTWARE, 0, CAMERA).getLastChange());
+ assertEquals(1, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getState());
+ assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getLastChange());
+ assertEquals(2, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getState());
+ assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getLastChange());
- assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 0, CAMERA));
- assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
}
@Test
public void testPersistence2Version2() throws IOException {
PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE8);
- assertEquals(1, ps.getState(HARDWARE, 0, MICROPHONE).getState());
- assertEquals(1234L, ps.getState(HARDWARE, 0, MICROPHONE).getLastChange());
- assertEquals(2, ps.getState(HARDWARE, 0, CAMERA).getState());
- assertEquals(1234L, ps.getState(HARDWARE, 0, CAMERA).getLastChange());
+ assertEquals(1, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getState());
+ assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getLastChange());
+ assertEquals(2, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getState());
+ assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getLastChange());
- assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 0, CAMERA));
- assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
- assertNull(ps.getState(SOFTWARE, 10, CAMERA));
- assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
- assertNull(ps.getState(HARDWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+ assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
}
private PersistedState getPersistedStateV2(String version2FilePath) throws IOException {
@@ -296,13 +296,15 @@
SensorPrivacyStateController sensorPrivacyStateController =
getSensorPrivacyStateControllerImpl();
- SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
- SensorState camState = sensorPrivacyStateController.getState(SOFTWARE, 0, CAMERA);
+ SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+ MICROPHONE);
+ SensorState camState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+ CAMERA);
assertEquals(SensorPrivacyManager.StateTypes.DISABLED, micState.getState());
assertEquals(SensorPrivacyManager.StateTypes.DISABLED, camState.getState());
- verify(persistedState, times(1)).getState(SOFTWARE, 0, MICROPHONE);
- verify(persistedState, times(1)).getState(SOFTWARE, 0, CAMERA);
+ verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE);
+ verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA);
} finally {
mockitoSession.finishMocking();
}
@@ -319,14 +321,16 @@
PersistedState persistedState = mock(PersistedState.class);
SensorState sensorState = mock(SensorState.class);
doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
- doReturn(sensorState).when(persistedState).getState(SOFTWARE, 0, MICROPHONE);
+ doReturn(sensorState).when(persistedState).getState(TOGGLE_TYPE_SOFTWARE, 0,
+ MICROPHONE);
doReturn(SensorPrivacyManager.StateTypes.ENABLED).when(sensorState).getState();
doReturn(0L).when(sensorState).getLastChange();
SensorPrivacyStateController sensorPrivacyStateController =
getSensorPrivacyStateControllerImpl();
- SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
+ SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+ MICROPHONE);
assertEquals(SensorPrivacyManager.StateTypes.ENABLED, micState.getState());
assertEquals(0L, micState.getLastChange());
@@ -349,13 +353,13 @@
SensorPrivacyStateController sensorPrivacyStateController =
getSensorPrivacyStateControllerImpl();
- sensorPrivacyStateController.setState(SOFTWARE, 0, MICROPHONE, true,
+ sensorPrivacyStateController.setState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE, true,
mock(Handler.class), changed -> {});
ArgumentCaptor<SensorState> captor = ArgumentCaptor.forClass(SensorState.class);
- verify(persistedState, times(1)).setState(eq(SOFTWARE), eq(0), eq(MICROPHONE),
- captor.capture());
+ verify(persistedState, times(1)).setState(eq(TOGGLE_TYPE_SOFTWARE), eq(0),
+ eq(MICROPHONE), captor.capture());
assertEquals(SensorPrivacyManager.StateTypes.ENABLED, captor.getValue().getState());
} finally {
mockitoSession.finishMocking();
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 68994e6..177d72b 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,5 +1,6 @@
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 8873f42..6789af4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -225,7 +225,11 @@
assertThat(packageUserState1.isSuspended(), is(true));
assertThat(packageUserState1.getSuspendParams().size(), is(1));
assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
- assertThat(packageUserState1.getSuspendParams().valueAt(0), is(nullValue()));
+ assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
+ assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
+ is(nullValue()));
+ assertThat(packageUserState1.getSuspendParams().valueAt(0).getLauncherExtras(),
+ is(nullValue()));
// Verify that the snapshot returns the same answers
ps1 = snapshot.mPackages.get(PACKAGE_NAME_1);
@@ -233,7 +237,11 @@
assertThat(packageUserState1.isSuspended(), is(true));
assertThat(packageUserState1.getSuspendParams().size(), is(1));
assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
- assertThat(packageUserState1.getSuspendParams().valueAt(0), is(nullValue()));
+ assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
+ assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
+ is(nullValue()));
+ assertThat(packageUserState1.getSuspendParams().valueAt(0).getLauncherExtras(),
+ is(nullValue()));
PackageSetting ps2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2);
PackageUserStateInternal packageUserState2 = ps2.readUserState(0);
@@ -315,14 +323,14 @@
.build();
ps1.modifyUserState(0).putSuspendParams( "suspendingPackage1",
- SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+ new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
ps1.modifyUserState(0).putSuspendParams( "suspendingPackage2",
- SuspendParams.getInstanceOrNull(dialogInfo2, appExtras2, launcherExtras2));
+ new SuspendParams(dialogInfo2, appExtras2, launcherExtras2));
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
watcher.verifyChangeReported("put package 1");
ps2.modifyUserState(0).putSuspendParams( "suspendingPackage3",
- SuspendParams.getInstanceOrNull(null, appExtras1, null));
+ new SuspendParams(null, appExtras1, null));
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
watcher.verifyChangeReported("put package 2");
@@ -686,7 +694,7 @@
.setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
.build();
origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1",
- SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+ new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
final PackageSetting testPkgSetting01 = new PackageSetting(
PACKAGE_NAME /*pkgName*/,
REAL_PACKAGE_NAME /*realPkgName*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 4dc9612..9ad503c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -85,7 +85,7 @@
oldUserState = new PackageUserStateImpl();
oldUserState.putSuspendParams("suspendingPackage",
- SuspendParams.getInstanceOrNull(null, new PersistableBundle(), null));
+ new SuspendParams(null, new PersistableBundle(), null));
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserStateImpl();
@@ -185,7 +185,7 @@
private static SuspendParams createSuspendParams(SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras) {
- return SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+ return new SuspendParams(dialogInfo, appExtras, launcherExtras);
}
private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey,
diff --git a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
index 95af1e1..6e03569 100644
--- a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
@@ -145,7 +145,7 @@
IntentFilter i = new IntentFilter("TEST_ACTION");
PreferredActivity a1 = new PreferredActivity(i, 1, components, component, true);
- r.addFilter(a1);
+ r.addFilter(null, a1);
watcher.verifyChangeReported("addFilter");
i.setPriority(i.getPriority() + 1);
watcher.verifyNoChangeReported("indepenent intent");
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 c94168c..1b92017 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -321,7 +321,7 @@
}
private void startSystem() {
- mService.systemReady();
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
// Grab the BatteryReceiver
ArgumentCaptor<BatteryReceiver> batCaptor = ArgumentCaptor.forClass(BatteryReceiver.class);
@@ -439,7 +439,7 @@
@Test
public void testGetDesiredScreenPolicy_WithVR() {
createService();
- mService.systemReady();
+ startSystem();
// Brighten up the screen
mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
null, null);
@@ -627,8 +627,7 @@
public void testWasDeviceIdleFor_true() {
int interval = 1000;
createService();
- mService.systemReady();
- mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ startSystem();
mService.onUserActivity();
advanceTime(interval + 1 /* just a little more */);
assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
@@ -638,8 +637,7 @@
public void testWasDeviceIdleFor_false() {
int interval = 1000;
createService();
- mService.systemReady();
- mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ startSystem();
mService.onUserActivity();
assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
}
@@ -647,8 +645,7 @@
@Test
public void testForceSuspend_putsDeviceToSleep() {
createService();
- mService.systemReady();
- mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ startSystem();
// Verify that we start awake
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -693,8 +690,7 @@
//
// TEST STARTS HERE
//
- mService.systemReady();
- mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ startSystem();
// Verify that we start awake
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -792,7 +788,7 @@
createService();
assertTrue(isAcquired[0]);
- mService.systemReady();
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
assertTrue(isAcquired[0]);
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -1231,7 +1227,7 @@
public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() {
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
createService();
- mService.systemReady();
+ startSystem();
mService.getBinderServiceInstance().wakeUp(mClock.now(),
PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
@@ -1444,7 +1440,7 @@
@Test
public void testSetPowerBoost_redirectsCallToNativeWrapper() {
createService();
- mService.systemReady();
+ startSystem();
mService.getBinderServiceInstance().setPowerBoost(Boost.INTERACTION, 1234);
@@ -1454,7 +1450,7 @@
@Test
public void testSetPowerMode_redirectsCallToNativeWrapper() {
createService();
- mService.systemReady();
+ startSystem();
// Enabled launch boost in BatterySaverController to allow setting launch mode.
when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(false);
@@ -1470,7 +1466,7 @@
@Test
public void testSetPowerMode_withLaunchBoostDisabledAndModeLaunch_ignoresCallToEnable() {
createService();
- mService.systemReady();
+ startSystem();
// Disables launch boost in BatterySaverController.
when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1486,7 +1482,7 @@
@Test
public void testSetPowerModeChecked_returnsNativeCallResult() {
createService();
- mService.systemReady();
+ startSystem();
// Disables launch boost in BatterySaverController.
when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1649,7 +1645,7 @@
@Test
public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
createService();
- mService.systemReady();
+ startSystem();
BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
when(mBatterySaverStateMachineMock.getFullBatterySaverPolicy())
.thenReturn(mockReturnConfig);
@@ -1664,7 +1660,7 @@
@Test
public void testSetFullPowerSavePolicy_callsStateMachine() {
createService();
- mService.systemReady();
+ startSystem();
BatterySaverPolicyConfig mockSetPolicyConfig =
new BatterySaverPolicyConfig.Builder().build();
when(mBatterySaverStateMachineMock.setFullBatterySaverPolicy(any())).thenReturn(true);
@@ -1678,7 +1674,7 @@
@Test
public void testLowPowerStandby_whenInactive_FgsWakeLockEnabled() {
createService();
- mService.systemReady();
+ startSystem();
WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
mService.setDeviceIdleModeInternal(true);
@@ -1689,7 +1685,7 @@
@Test
public void testLowPowerStandby_whenActive_FgsWakeLockDisabled() {
createService();
- mService.systemReady();
+ startSystem();
WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
mService.setDeviceIdleModeInternal(true);
@@ -1701,7 +1697,7 @@
@Test
public void testLowPowerStandby_whenActive_FgsWakeLockEnabledIfAllowlisted() {
createService();
- mService.systemReady();
+ startSystem();
WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
mService.setDeviceIdleModeInternal(true);
@@ -1714,7 +1710,7 @@
@Test
public void testLowPowerStandby_whenActive_BoundTopWakeLockDisabled() {
createService();
- mService.systemReady();
+ startSystem();
WakeLock wakeLock = acquireWakeLock("BoundTopWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_BOUND_TOP);
mService.setDeviceIdleModeInternal(true);
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
index 16cfd13..6bdd88c 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -31,7 +31,6 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -49,7 +48,6 @@
import android.os.IHwBinder;
import android.os.IHwInterface;
import android.os.RemoteException;
-import android.system.OsConstants;
import org.junit.After;
import org.junit.Before;
@@ -60,6 +58,7 @@
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
@RunWith(Parameterized.class)
public class SoundHw2CompatTest {
@@ -240,14 +239,15 @@
(android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
final int handle = 29;
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model =
+ new AtomicReference<>();
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
doAnswer(invocation -> {
+ // We need to dup the model, as it gets invalidated after the call returns.
+ model.set(TestUtil.dupModel_2_1(invocation.getArgument(0)));
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
resultCallback = invocation.getArgument(3);
@@ -259,10 +259,9 @@
assertEquals(handle,
mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
- verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- anyInt(), any());
+ verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any());
- TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ TestUtil.validateGenericSoundModel_2_1(model.get());
validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
return handle;
}
@@ -355,14 +354,16 @@
(android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
final int handle = 29;
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model =
+ new AtomicReference<>();
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
doAnswer(invocation -> {
+ // We need to dup the model, as it gets invalidated after the call returns.
+ model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0)));
+
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
resultCallback = invocation.getArgument(3);
@@ -374,10 +375,10 @@
assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
canonicalCallback));
- verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- anyInt(), any());
+ verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(),
+ any());
- TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ TestUtil.validatePhraseSoundModel_2_1(model.get());
validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
return handle;
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index e687a2a..30b4a59 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -47,6 +47,7 @@
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
@@ -82,6 +83,19 @@
HidlMemoryUtil.hidlMemoryToByteArray(model.data));
}
+ static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup =
+ new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel();
+ dup.header = model.header;
+ try {
+ dup.data = model.data.dup();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return dup;
+ }
+
private static void validateSoundModel_2_0(
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
assertEquals(type, model.type);
@@ -121,6 +135,15 @@
validatePhrases_2_0(model.phrases);
}
+ static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup =
+ new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel();
+ dup.common = dupModel_2_1(model.common);
+ dup.phrases = model.phrases;
+ return dup;
+ }
+
static void validatePhraseSoundModel_2_0(
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
validateSoundModel_2_0(model.common,
@@ -190,31 +213,27 @@
properties.maxKeyPhrases = 567;
properties.maxUsers = 678;
properties.recognitionModes =
- RecognitionMode.VOICE_TRIGGER
- | RecognitionMode.USER_IDENTIFICATION
- | RecognitionMode.USER_AUTHENTICATION
- | RecognitionMode.GENERIC_TRIGGER;
+ RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER;
properties.captureTransition = true;
properties.maxBufferMs = 321;
properties.concurrentCapture = supportConcurrentCapture;
properties.triggerInEvent = true;
properties.powerConsumptionMw = 432;
properties.supportedModelArch = "supportedModelArch";
- properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION;
+ properties.audioCapabilities =
+ AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
return properties;
}
- static void validateDefaultProperties(Properties properties,
- boolean supportConcurrentCapture) {
+ static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) {
validateDefaultProperties(properties, supportConcurrentCapture,
AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
"supportedModelArch");
}
- static void validateDefaultProperties(Properties properties,
- boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
- @NonNull String supportedModelArch) {
+ static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture,
+ @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) {
assertEquals("implementor", properties.implementor);
assertEquals("description", properties.description);
assertEquals(123, properties.version);
@@ -222,10 +241,9 @@
assertEquals(456, properties.maxSoundModels);
assertEquals(567, properties.maxKeyPhrases);
assertEquals(678, properties.maxUsers);
- assertEquals(RecognitionMode.GENERIC_TRIGGER
- | RecognitionMode.USER_AUTHENTICATION
- | RecognitionMode.USER_IDENTIFICATION
- | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+ assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER,
+ properties.recognitionModes);
assertTrue(properties.captureTransition);
assertEquals(321, properties.maxBufferMs);
assertEquals(supportConcurrentCapture, properties.concurrentCapture);
@@ -246,8 +264,8 @@
config.phraseRecognitionExtras[0].levels[0].userId = 234;
config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
config.data = new byte[]{5, 4, 3, 2, 1};
- config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION;
+ config.audioCapabilities =
+ AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
return config;
}
@@ -295,13 +313,12 @@
int captureHandle) {
validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
- assertEquals(AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+ assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+ config.audioCapabilities);
}
static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
- int hwHandle,
- int status) {
+ int hwHandle, int status) {
android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
halEvent.status = status;
@@ -351,8 +368,7 @@
return event;
}
- static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
- int hwHandle,
+ static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle,
int status) {
ISoundTriggerHwCallback.RecognitionEvent halEvent =
new ISoundTriggerHwCallback.RecognitionEvent();
@@ -386,8 +402,7 @@
PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
extra.id = 123;
extra.confidenceLevel = 52;
- extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
- | RecognitionMode.GENERIC_TRIGGER;
+ extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
ConfidenceLevel level = new ConfidenceLevel();
level.userId = 31;
level.levelPercent = 43;
@@ -396,8 +411,8 @@
return event;
}
- static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
- createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
+ int hwHandle, int status) {
android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
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 ffc10d7..b3d6b3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3125,7 +3125,7 @@
assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
verify(app2.mClient, atLeastOnce()).insetsChanged(insetsStateCaptor.capture(), anyBoolean(),
anyBoolean());
- assertFalse(insetsStateCaptor.getAllValues().get(0).peekSource(ITYPE_IME).isVisible());
+ assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index c7a1b07..e387615 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -274,7 +274,7 @@
// IME cannot be the IME target.
ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
- InsetsSourceProvider statusBarProvider =
+ WindowContainerInsetsSourceProvider statusBarProvider =
getController().getSourceProvider(ITYPE_STATUS_BAR);
statusBarProvider.setWindowContainer(statusBar, null, ((displayFrames, windowState, rect) ->
rect.set(0, 1, 2, 3)));
@@ -336,7 +336,8 @@
public void testTransientVisibilityOfFixedRotationState() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final InsetsSourceProvider provider = getController().getSourceProvider(ITYPE_STATUS_BAR);
+ final WindowContainerInsetsSourceProvider provider = getController()
+ .getSourceProvider(ITYPE_STATUS_BAR);
provider.setWindowContainer(statusBar, null, null);
final InsetsState rotatedState = new InsetsState(app.getInsetsState(),
@@ -366,7 +367,7 @@
assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
- getController().updateAboveInsetsState(statusBar, true /* notifyInsetsChange */);
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
@@ -389,7 +390,7 @@
assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
- getController().updateAboveInsetsState(app, true /* notifyInsetsChange */);
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
@@ -410,9 +411,8 @@
null);
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
null);
- getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */);
- getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */);
- getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */);
+
+ getController().updateAboveInsetsState(false /* notifyInsetsChange */);
// ime is below others.
assertNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
@@ -422,7 +422,7 @@
assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
ime.getParent().positionChildAt(POSITION_TOP, ime, true /* includingParents */);
- getController().updateAboveInsetsState(ime, true /* notifyInsetsChange */);
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
// ime is above others.
assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index b815c38..7b38a95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -31,6 +31,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -2128,6 +2129,136 @@
APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
}
+ @Test
+ public void testIsEligibleForLetterboxEducation_educationNotEnabled_returnsFalse() {
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ assertFalse(mActivity.isEligibleForLetterboxEducation());
+ }
+
+ @Test
+ public void testIsEligibleForLetterboxEducation_notEligibleForFixedOrientation_returnsFalse() {
+ setUpDisplaySizeWithApp(1000, 2500);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ assertFalse(mActivity.isEligibleForLetterboxEducation());
+ }
+
+ @Test
+ public void testIsEligibleForLetterboxEducation_windowingModeMultiWindow_returnsFalse() {
+ // Support non resizable in multi window
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ setUpDisplaySizeWithApp(1000, 1200);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+ // Non-resizable landscape activity
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+ final Rect originalBounds = new Rect(mActivity.getBounds());
+
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP,
+ false /*moveParents*/, "test");
+ organizer.mPrimary.setBounds(0, 0, 1000, 600);
+
+ assertFalse(mActivity.isEligibleForLetterboxEducation());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+ }
+
+ @Test
+ public void testIsEligibleForLetterboxEducation_fixedOrientationLandscape_returnsFalse() {
+ setUpDisplaySizeWithApp(1000, 2500);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertFalse(mActivity.isEligibleForLetterboxEducation());
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
+ public void testIsEligibleForLetterboxEducation_hasStartingWindow_returnsFalseUntilRemoved() {
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.mStartingData = mock(StartingData.class);
+ mActivity.attachStartingWindow(
+ createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+ mActivity));
+
+ assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+ // Verify that after removing the starting window isEligibleForLetterboxEducation returns
+ // true and mTask.dispatchTaskInfoChangedIfNeeded is called.
+ spyOn(mTask);
+ mActivity.removeStartingWindow();
+
+ assertTrue(mActivity.isEligibleForLetterboxEducation());
+ verify(mTask).dispatchTaskInfoChangedIfNeeded(true);
+ }
+
+ @Test
+ public void testIsEligibleForLetterboxEducation_hasStartingWindowAndEducationNotEnabled() {
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.mStartingData = mock(StartingData.class);
+ mActivity.attachStartingWindow(
+ createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+ mActivity));
+
+ assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+ // Verify that after removing the starting window isEligibleForLetterboxEducation still
+ // returns false and mTask.dispatchTaskInfoChangedIfNeeded isn't called.
+ spyOn(mTask);
+ mActivity.removeStartingWindow();
+
+ assertFalse(mActivity.isEligibleForLetterboxEducation());
+ verify(mTask, never()).dispatchTaskInfoChangedIfNeeded(true);
+ }
+
+ @Test
+ public void testIsEligibleForLetterboxEducation_letterboxedForFixedOrientation_returnsTrue() {
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ assertTrue(mActivity.isEligibleForLetterboxEducation());
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
+ public void testIsEligibleForLetterboxEducation_sizeCompatAndEligibleForFixedOrientation() {
+ setUpDisplaySizeWithApp(1000, 2500);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ assertTrue(mActivity.isEligibleForLetterboxEducation());
+ assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertTrue(mActivity.inSizeCompatMode());
+ }
+
/**
* Tests that all three paths in which aspect ratio logic can be applied yield the same
* result, which is that aspect ratio is respected on app bounds. The three paths are
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
similarity index 96%
rename from services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
index c61b88b..10e4292 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
@@ -42,19 +42,19 @@
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-public class InsetsSourceProviderTest extends WindowTestsBase {
+public class WindowContainerInsetsSourceProviderTest extends WindowTestsBase {
private InsetsSource mSource = new InsetsSource(ITYPE_STATUS_BAR);
- private InsetsSourceProvider mProvider;
+ private WindowContainerInsetsSourceProvider mProvider;
private InsetsSource mImeSource = new InsetsSource(ITYPE_IME);
- private InsetsSourceProvider mImeProvider;
+ private WindowContainerInsetsSourceProvider mImeProvider;
@Before
public void setUp() throws Exception {
mSource.setVisible(true);
- mProvider = new InsetsSourceProvider(mSource,
+ mProvider = new WindowContainerInsetsSourceProvider(mSource,
mDisplayContent.getInsetsStateController(), mDisplayContent);
- mImeProvider = new InsetsSourceProvider(mImeSource,
+ mImeProvider = new WindowContainerInsetsSourceProvider(mImeSource,
mDisplayContent.getInsetsStateController(), mDisplayContent);
}
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 41a59eb..a442de5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_1;
+import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_2;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -68,6 +70,7 @@
import android.platform.test.annotations.Presubmit;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
+import android.view.InsetsSource;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -478,7 +481,7 @@
windowState.mSurfaceAnimator).getAnimationType();
assertTrue(parent.isAnimating(CHILDREN));
- windowState.setControllableInsetProvider(mock(InsetsSourceProvider.class));
+ windowState.setControllableInsetProvider(mock(WindowContainerInsetsSourceProvider.class));
assertFalse(parent.isAnimating(CHILDREN));
}
@@ -1267,6 +1270,140 @@
verify(prevSnapshot).destroy(t);
}
+ @Test
+ public void testAddLocalInsetsSourceProvider() {
+ /*
+ ___ rootTask _______________________________________________
+ | | | |
+ activity0 container navigationBarInsetsProvider1 navigationBarInsetsProvider2
+ / \
+ activity1 activity2
+ */
+ final Task rootTask = createTask(mDisplayContent);
+
+ final ActivityRecord activity0 = createActivityRecord(mDisplayContent,
+ createTaskInRootTask(rootTask, 0 /* userId */));
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs.setTitle("AppWindow0");
+ activity0.addWindow(createWindowState(attrs, activity0));
+
+ final Task container = createTaskInRootTask(rootTask, 0);
+ final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
+ createTaskInRootTask(container, 0 /* userId */));
+ final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs1.setTitle("AppWindow1");
+ activity1.addWindow(createWindowState(attrs1, activity1));
+
+ final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
+ createTaskInRootTask(container, 0 /* userId */));
+ final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs2.setTitle("AppWindow2");
+ activity2.addWindow(createWindowState(attrs2, activity2));
+ Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
+ Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
+
+ rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
+ new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
+ new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
+
+ InsetsSource navigationBarInsetsProvider1Source = new InsetsSource(
+ ITYPE_LOCAL_NAVIGATION_BAR_1);
+ navigationBarInsetsProvider1Source.setFrame(navigationBarInsetsRect1);
+ navigationBarInsetsProvider1Source.setVisible(true);
+ InsetsSource navigationBarInsetsProvider2Source = new InsetsSource(
+ ITYPE_LOCAL_NAVIGATION_BAR_2);
+ navigationBarInsetsProvider2Source.setFrame(navigationBarInsetsRect2);
+ navigationBarInsetsProvider2Source.setVisible(true);
+
+ activity0.forAllWindows(window -> {
+ assertEquals(navigationBarInsetsRect1,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+ assertEquals(null,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+ }, true);
+ activity1.forAllWindows(window -> {
+ assertEquals(navigationBarInsetsRect1,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+ assertEquals(navigationBarInsetsRect2,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ }, true);
+ activity2.forAllWindows(window -> {
+ assertEquals(navigationBarInsetsRect1,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+ assertEquals(navigationBarInsetsRect2,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ }, true);
+ }
+
+ @Test
+ public void testRemoveLocalInsetsSourceProvider() {
+ /*
+ ___ rootTask _______________________________________________
+ | | | |
+ activity0 container navigationBarInsetsProvider1 navigationBarInsetsProvider2
+ / \
+ activity1 activity2
+ */
+ final Task rootTask = createTask(mDisplayContent);
+
+ final ActivityRecord activity0 = createActivityRecord(mDisplayContent,
+ createTaskInRootTask(rootTask, 0 /* userId */));
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs.setTitle("AppWindow0");
+ activity0.addWindow(createWindowState(attrs, activity0));
+
+ final Task container = createTaskInRootTask(rootTask, 0);
+ final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
+ createTaskInRootTask(container, 0 /* userId */));
+ final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs1.setTitle("AppWindow1");
+ activity1.addWindow(createWindowState(attrs1, activity1));
+
+ final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
+ createTaskInRootTask(container, 0 /* userId */));
+ final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs2.setTitle("AppWindow2");
+ activity2.addWindow(createWindowState(attrs2, activity2));
+
+ activity2.addWindow(createWindowState(attrs2, activity2));
+ Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
+ Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
+
+ rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
+ new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
+ new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
+ mDisplayContent.getInsetsStateController().onPostLayout();
+ rootTask.removeLocalInsetsSourceProvider(new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ mDisplayContent.getInsetsStateController().onPostLayout();
+
+ activity0.forAllWindows(window -> {
+ assertEquals(null,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+ assertEquals(null,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+ }, true);
+ activity1.forAllWindows(window -> {
+ assertEquals(null,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+ assertEquals(navigationBarInsetsRect2,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ }, true);
+ activity2.forAllWindows(window -> {
+ assertEquals(null,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+ assertEquals(navigationBarInsetsRect2,
+ window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ }, true);
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
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 459e3a5..ef600f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -899,7 +899,7 @@
assertTrue(mDisplayContent.shouldImeAttachedToApp());
controller.getImeSourceProvider().scheduleShowImePostLayout(app);
controller.getImeSourceProvider().getSource().setVisible(true);
- controller.updateAboveInsetsState(imeWindow, false);
+ controller.updateAboveInsetsState(false);
// Expect all app windows behind IME can receive IME insets visible.
assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index fd9b995..42a5af7 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -36,7 +36,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
/**
@@ -107,12 +106,6 @@
return false;
}
- /**
- * List of connected MIDI devices
- */
- private final HashMap<String, UsbMidiDevice>
- mMidiDevices = new HashMap<String, UsbMidiDevice>();
-
// UsbMidiDevice for USB peripheral mode (gadget) device
private UsbMidiDevice mPeripheralMidiDevice = null;
@@ -256,45 +249,6 @@
}
}
- // look for MIDI devices
- boolean hasMidi = parser.hasMIDIInterface();
- int midiNumInputs = parser.calculateNumLegacyMidiInputs();
- int midiNumOutputs = parser.calculateNumLegacyMidiOutputs();
- if (DEBUG) {
- Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
- Slog.d(TAG, "midiNumInputs: " + midiNumInputs + " midiNumOutputs:" + midiNumOutputs);
- }
- if (hasMidi && mHasMidiFeature) {
- int device = 0;
- Bundle properties = new Bundle();
- String manufacturer = usbDevice.getManufacturerName();
- String product = usbDevice.getProductName();
- String version = usbDevice.getVersion();
- String name;
- if (manufacturer == null || manufacturer.isEmpty()) {
- name = product;
- } else if (product == null || product.isEmpty()) {
- name = manufacturer;
- } else {
- name = manufacturer + " " + product;
- }
- properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
- properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
- properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
- properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
- properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
- usbDevice.getSerialNumber());
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
- properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
-
- UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
- cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs);
- if (usbMidiDevice != null) {
- mMidiDevices.put(deviceAddress, usbMidiDevice);
- }
- }
-
logDevices("deviceAdded()");
if (DEBUG) {
@@ -315,13 +269,6 @@
selectDefaultDevice(); // if there any external devices left, select one of them
}
- // MIDI
- UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
- if (usbMidiDevice != null) {
- Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
- IoUtils.closeQuietly(usbMidiDevice);
- }
-
logDevices("usbDeviceRemoved()");
}
@@ -377,12 +324,6 @@
usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES);
}
- for (String deviceAddr : mMidiDevices.keySet()) {
- // A UsbMidiDevice does not have a handle to the UsbDevice anymore
- mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "midi_devices",
- UsbAlsaManagerProto.MIDI_DEVICES);
- }
-
dump.end(token);
}
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
similarity index 65%
rename from services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
rename to services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 13d404c..0fa79df 100644
--- a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -20,6 +20,7 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceServer;
@@ -43,16 +44,20 @@
import java.util.ArrayList;
/**
- * A MIDI device that opens device connections to MIDI 2.0 endpoints.
+ * Opens device connections to MIDI 1.0 or MIDI 2.0 endpoints.
+ * This endpoint will not use ALSA and opens a UsbDeviceConnection directly.
*/
-public final class UsbUniversalMidiDevice implements Closeable {
- private static final String TAG = "UsbUniversalMidiDevice";
+public final class UsbDirectMidiDevice implements Closeable {
+ private static final String TAG = "UsbDirectMidiDevice";
private static final boolean DEBUG = false;
private Context mContext;
private UsbDevice mUsbDevice;
private UsbDescriptorParser mParser;
private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
+ private final boolean mIsUniversalMidiDevice;
+ private final String mUniqueUsbDeviceIdentifier;
+ private final boolean mShouldCallSetInterface;
// USB outputs are MIDI inputs
private final InputReceiverProxy[] mMidiInputPortReceivers;
@@ -64,6 +69,11 @@
// event schedulers for each input port of the physical device
private MidiEventScheduler[] mEventSchedulers;
+ // Arbitrary number for timeout to not continue sending/receiving number from
+ // an inactive device. This number tries to balances the number of cycles and
+ // not being permanently stuck.
+ private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 100;
+
private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
@@ -74,6 +84,8 @@
private final Object mLock = new Object();
private boolean mIsOpen;
+ private final UsbMidiPacketConverter mUsbMidiPacketConverter = new UsbMidiPacketConverter();
+
private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
@Override
@@ -140,13 +152,15 @@
}
/**
- * Creates an UsbUniversalMidiDevice based on the input parameters. Read/Write streams
+ * Creates an UsbDirectMidiDevice based on the input parameters. Read/Write streams
* will be created individually as some devices don't have the same number of
* inputs and outputs.
*/
- public static UsbUniversalMidiDevice create(Context context, UsbDevice usbDevice,
- UsbDescriptorParser parser) {
- UsbUniversalMidiDevice midiDevice = new UsbUniversalMidiDevice(usbDevice, parser);
+ public static UsbDirectMidiDevice create(Context context, UsbDevice usbDevice,
+ UsbDescriptorParser parser, boolean isUniversalMidiDevice,
+ String uniqueUsbDeviceIdentifier) {
+ UsbDirectMidiDevice midiDevice = new UsbDirectMidiDevice(usbDevice, parser,
+ isUniversalMidiDevice, uniqueUsbDeviceIdentifier);
if (!midiDevice.register(context)) {
IoUtils.closeQuietly(midiDevice);
Log.e(TAG, "createDeviceServer failed");
@@ -155,11 +169,22 @@
return midiDevice;
}
- private UsbUniversalMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser) {
+ private UsbDirectMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser,
+ boolean isUniversalMidiDevice, String uniqueUsbDeviceIdentifier) {
mUsbDevice = usbDevice;
mParser = parser;
+ mUniqueUsbDeviceIdentifier = uniqueUsbDeviceIdentifier;
+ mIsUniversalMidiDevice = isUniversalMidiDevice;
- mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+ // Set interface should only be called when alternate interfaces exist.
+ // Otherwise, USB devices may not handle this gracefully.
+ mShouldCallSetInterface = (parser.calculateMidiInterfaceDescriptorsCount() > 1);
+
+ if (isUniversalMidiDevice) {
+ mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+ } else {
+ mUsbInterfaces = parser.findLegacyMidiInterfaceDescriptors();
+ }
int numInputs = 0;
int numOutputs = 0;
@@ -182,8 +207,8 @@
mNumInputs = numInputs;
mNumOutputs = numOutputs;
- Log.d(TAG, "Created UsbUniversalMidiDevice with " + numInputs + " inputs and "
- + numOutputs + " outputs");
+ Log.d(TAG, "Created UsbDirectMidiDevice with " + numInputs + " inputs and "
+ + numOutputs + " outputs. isUniversalMidiDevice: " + isUniversalMidiDevice);
// Create MIDI port receivers based on the number of output ports. The
// output of USB is the input of MIDI.
@@ -218,23 +243,25 @@
if (doesInterfaceContainInput
&& doesInterfaceContainOutput) {
UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-
- // The ALSA does not handle switching to the MIDI 2.0 interface correctly
- // and stops exposing /dev/snd/midiC1D0 after calling connection.setInterface().
- // Thus, simply use the control interface (interface zero).
+ UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+ if (!updateUsbInterface(usbInterface, connection)) {
+ continue;
+ }
int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
- 0,
+ interfaceDescriptor.getInterfaceNumber(),
interfaceDescriptor.getAlternateSetting());
+
connection.close();
return defaultMidiProtocol;
}
}
- Log.d(TAG, "Cannot find interface with both input and output endpoints");
+ Log.w(TAG, "Cannot find interface with both input and output endpoints");
return MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
}
private boolean openLocked() {
+ Log.d(TAG, "openLocked()");
UsbManager manager = mContext.getSystemService(UsbManager.class);
mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
@@ -258,8 +285,10 @@
}
if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
- connection.setInterface(interfaceDescriptor.toAndroid(mParser));
- connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true);
+ UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+ if (!updateUsbInterface(usbInterface, connection)) {
+ continue;
+ }
mUsbDeviceConnections.add(connection);
mInputUsbEndpoints.add(inputEndpoints);
mOutputUsbEndpoints.add(outputEndpoints);
@@ -283,14 +312,17 @@
for (int endpointIndex = 0;
endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
endpointIndex++) {
- final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
- final UsbEndpoint epF = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
- final int portF = portNumber;
+ final UsbDeviceConnection connectionFinal =
+ mUsbDeviceConnections.get(connectionIndex);
+ final UsbEndpoint endpointFinal =
+ mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+ final int portFinal = portNumber;
- new Thread("UsbUniversalMidiDevice input thread " + portF) {
+ new Thread("UsbDirectMidiDevice input thread " + portFinal) {
@Override
public void run() {
- byte[] inputBuffer = new byte[epF.getMaxPacketSize()];
+ byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
+ Log.d(TAG, "input buffer size: " + inputBuffer.length);
try {
while (true) {
// Record time of event immediately after waking.
@@ -298,20 +330,34 @@
synchronized (mLock) {
if (!mIsOpen) break;
- int nRead = connectionF.bulkTransfer(epF, inputBuffer,
- inputBuffer.length, 0);
-
- // For USB, each 32 bit word of a UMP is
- // sent with the least significant byte first.
- swapEndiannessPerWord(inputBuffer, inputBuffer.length);
+ int nRead = connectionFinal.bulkTransfer(endpointFinal,
+ inputBuffer, inputBuffer.length,
+ BULK_TRANSFER_TIMEOUT_MILLISECONDS);
if (nRead > 0) {
if (DEBUG) {
- logByteArray("Input ", inputBuffer, 0,
- nRead);
+ logByteArray("Input before conversion ", inputBuffer,
+ 0, nRead);
}
- outputReceivers[portF].send(inputBuffer, 0, nRead,
- timestamp);
+ byte[] convertedArray;
+ if (mIsUniversalMidiDevice) {
+ // For USB, each 32 bit word of a UMP is
+ // sent with the least significant byte first.
+ convertedArray = swapEndiannessPerWord(inputBuffer,
+ nRead);
+ } else {
+ convertedArray =
+ mUsbMidiPacketConverter.usbMidiToRawMidi(
+ inputBuffer, nRead);
+ }
+
+ if (DEBUG) {
+ logByteArray("Input after conversion ", convertedArray,
+ 0, convertedArray.length);
+ }
+
+ outputReceivers[portFinal].send(convertedArray, 0,
+ convertedArray.length, timestamp);
}
}
}
@@ -333,19 +379,20 @@
for (int endpointIndex = 0;
endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
endpointIndex++) {
- final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
- final UsbEndpoint epF =
+ final UsbDeviceConnection connectionFinal =
+ mUsbDeviceConnections.get(connectionIndex);
+ final UsbEndpoint endpointFinal =
mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
- final int portF = portNumber;
- final MidiEventScheduler eventSchedulerF = mEventSchedulers[portF];
+ final int portFinal = portNumber;
+ final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
- new Thread("UsbUniversalMidiDevice output thread " + portF) {
+ new Thread("UsbDirectMidiDevice output thread " + portFinal) {
@Override
public void run() {
while (true) {
MidiEvent event;
try {
- event = (MidiEvent) eventSchedulerF.waitNextEvent();
+ event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
} catch (InterruptedException e) {
// try again
continue;
@@ -354,16 +401,32 @@
break;
}
- // For USB, each 32 bit word of a UMP is
- // sent with the least significant byte first.
- swapEndiannessPerWord(event.data, event.count);
-
if (DEBUG) {
- logByteArray("Output ", event.data, 0,
+ logByteArray("Output before conversion ", event.data, 0,
event.count);
}
- connectionF.bulkTransfer(epF, event.data, event.count, 0);
- eventSchedulerF.addEventToPool(event);
+
+ byte[] convertedArray;
+ if (mIsUniversalMidiDevice) {
+ // For USB, each 32 bit word of a UMP is
+ // sent with the least significant byte first.
+ convertedArray = swapEndiannessPerWord(event.data,
+ event.count);
+ } else {
+ convertedArray =
+ mUsbMidiPacketConverter.rawMidiToUsbMidi(
+ event.data, event.count);
+ }
+
+ if (DEBUG) {
+ logByteArray("Output after conversion ", convertedArray, 0,
+ convertedArray.length);
+ }
+
+ connectionFinal.bulkTransfer(endpointFinal, convertedArray,
+ convertedArray.length,
+ BULK_TRANSFER_TIMEOUT_MILLISECONDS);
+ eventSchedulerFinal.addEventToPool(event);
}
Log.d(TAG, "output thread exit");
}
@@ -381,11 +444,15 @@
mContext = context;
MidiManager midiManager = context.getSystemService(MidiManager.class);
if (midiManager == null) {
- Log.e(TAG, "No MidiManager in UsbUniversalMidiDevice.create()");
+ Log.e(TAG, "No MidiManager in UsbDirectMidiDevice.create()");
return false;
}
- mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+ if (mIsUniversalMidiDevice) {
+ mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+ } else {
+ mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UNKNOWN;
+ }
Bundle properties = new Bundle();
String manufacturer = mUsbDevice.getManufacturerName();
@@ -397,8 +464,15 @@
} else if (product == null || product.isEmpty()) {
name = manufacturer;
} else {
- name = manufacturer + " " + product + " MIDI 2.0";
+ name = manufacturer + " " + product;
}
+ name += "#" + mUniqueUsbDeviceIdentifier;
+ if (mIsUniversalMidiDevice) {
+ name += " MIDI 2.0";
+ } else {
+ name += " MIDI 1.0";
+ }
+ Log.e(TAG, name);
properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
@@ -430,10 +504,13 @@
}
private void closeLocked() {
+ Log.d(TAG, "closeLocked()");
for (int i = 0; i < mEventSchedulers.length; i++) {
mMidiInputPortReceivers[i].setReceiver(null);
mEventSchedulers[i].close();
}
+ mEventSchedulers = null;
+
for (UsbDeviceConnection connection : mUsbDeviceConnections) {
connection.close();
}
@@ -444,15 +521,19 @@
mIsOpen = false;
}
- private void swapEndiannessPerWord(byte[] array, int size) {
- for (int i = 0; i + 3 < size; i += 4) {
- byte tmp = array[i];
- array[i] = array[i + 3];
- array[i + 3] = tmp;
- tmp = array[i + 1];
- array[i + 1] = array[i + 2];
- array[i + 2] = tmp;
+ private byte[] swapEndiannessPerWord(byte[] inputArray, int size) {
+ int numberOfExcessBytes = size & 3;
+ if (numberOfExcessBytes != 0) {
+ Log.e(TAG, "size not multiple of 4: " + size);
}
+ byte[] outputArray = new byte[size - numberOfExcessBytes];
+ for (int i = 0; i + 3 < size; i += 4) {
+ outputArray[i] = inputArray[i + 3];
+ outputArray[i + 1] = inputArray[i + 2];
+ outputArray[i + 2] = inputArray[i + 1];
+ outputArray[i + 3] = inputArray[i];
+ }
+ return outputArray;
}
private static void logByteArray(String prefix, byte[] value, int offset, int count) {
@@ -465,4 +546,24 @@
}
Log.d(TAG, builder.toString());
}
+
+ private boolean updateUsbInterface(UsbInterface usbInterface,
+ UsbDeviceConnection connection) {
+ if (usbInterface == null) {
+ Log.e(TAG, "Usb Interface is null");
+ return false;
+ }
+ if (!connection.claimInterface(usbInterface, true)) {
+ Log.e(TAG, "Can't claim interface");
+ return false;
+ }
+ if (mShouldCallSetInterface) {
+ if (!connection.setInterface(usbInterface)) {
+ Log.w(TAG, "Can't set interface");
+ }
+ } else {
+ Log.w(TAG, "no alternate interface");
+ }
+ return true;
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 94cc826..a70b0332 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -50,9 +50,12 @@
import libcore.io.IoUtils;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
+import java.util.Random;
/**
* UsbHostManager manages USB state in host mode.
@@ -94,10 +97,12 @@
private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
/**
- * List of connected MIDI devices
+ * List of connected MIDI devices. Key on deviceAddress.
*/
- private final HashMap<String, UsbUniversalMidiDevice>
- mMidiDevices = new HashMap<String, UsbUniversalMidiDevice>();
+ private final HashMap<String, ArrayList<UsbDirectMidiDevice>>
+ mMidiDevices = new HashMap<String, ArrayList<UsbDirectMidiDevice>>();
+ private final HashSet<String> mMidiUniqueCodes = new HashSet<String>();
+ private final Random mRandom = new Random();
private final boolean mHasMidiFeature;
/*
@@ -423,15 +428,35 @@
mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
if (mHasMidiFeature) {
+ // Use a 3 digit code to associate MIDI devices with one another.
+ // Each MIDI device already has mId for uniqueness. mId is generated
+ // sequentially. For clarity, this code is not generated sequentially.
+ String uniqueUsbDeviceIdentifier = generateNewUsbDeviceIdentifier();
+
+ ArrayList<UsbDirectMidiDevice> midiDevices =
+ new ArrayList<UsbDirectMidiDevice>();
if (parser.containsUniversalMidiDeviceEndpoint()) {
- UsbUniversalMidiDevice midiDevice = UsbUniversalMidiDevice.create(mContext,
- newDevice, parser);
+ UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+ newDevice, parser, true, uniqueUsbDeviceIdentifier);
if (midiDevice != null) {
- mMidiDevices.put(deviceAddress, midiDevice);
+ midiDevices.add(midiDevice);
} else {
Slog.e(TAG, "Universal Midi Device is null.");
}
}
+ if (parser.containsLegacyMidiDeviceEndpoint()) {
+ UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+ newDevice, parser, false, uniqueUsbDeviceIdentifier);
+ if (midiDevice != null) {
+ midiDevices.add(midiDevice);
+ } else {
+ Slog.e(TAG, "Legacy Midi Device is null.");
+ }
+ }
+
+ if (!midiDevices.isEmpty()) {
+ mMidiDevices.put(deviceAddress, midiDevices);
+ }
}
// Tracking
@@ -469,10 +494,13 @@
mPermissionManager.usbDeviceRemoved(device);
// MIDI
- UsbUniversalMidiDevice midiDevice = mMidiDevices.remove(deviceAddress);
- if (midiDevice != null) {
- Slog.i(TAG, "USB Universal MIDI Device Removed: " + deviceAddress);
- IoUtils.closeQuietly(midiDevice);
+ ArrayList<UsbDirectMidiDevice> midiDevices =
+ mMidiDevices.remove(deviceAddress);
+ for (UsbDirectMidiDevice midiDevice : midiDevices) {
+ if (midiDevice != null) {
+ Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress);
+ IoUtils.closeQuietly(midiDevice);
+ }
}
getCurrentUserSettings().usbDeviceRemoved(device);
@@ -606,6 +634,19 @@
return true;
}
+ // Generate a 3 digit code.
+ private String generateNewUsbDeviceIdentifier() {
+ String code;
+ do {
+ code = "";
+ for (int i = 0; i < 3; i++) {
+ code += mRandom.nextInt(10);
+ }
+ } while (mMidiUniqueCodes.contains(code));
+ mMidiUniqueCodes.add(code);
+ return code;
+ }
+
private native void monitorUsbHostBus();
private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
}
diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
new file mode 100644
index 0000000..7c93c76
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 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.usb;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Converts between MIDI packets and USB MIDI 1.0 packets.
+ */
+public class UsbMidiPacketConverter {
+
+ // Refer to Table 4-1 in USB MIDI 1.0 spec.
+ private static final int[] PAYLOAD_SIZE = new int[]{
+ /* 0x00 */ -1, // Miscellaneous function codes. Reserved for future extensions.
+ /* 0x01 */ -1, // Cable events. Reserved for future expansion.
+ /* 0x02 */ 2, // Two-byte System Common messages like MTC, SongSelect, etc
+ /* 0x03 */ 3, // Three-byte System Common messages like SPP, etc.
+ /* 0x04 */ 3, // SysEx starts or continues
+ /* 0x05 */ 1, // Single-byte System Common Message or single-byte SysEx ends.
+ /* 0x06 */ 2, // SysEx ends with following two bytes.
+ /* 0x07 */ 3, // SysEx ends with following three bytes.
+ /* 0x08 */ 3, // Note-off
+ /* 0x09 */ 3, // Note-on
+ /* 0x0a */ 3, // Poly-KeyPress
+ /* 0x0b */ 3, // Control Change
+ /* 0x0c */ 2, // Program Change
+ /* 0x0d */ 2, // Channel Pressure
+ /* 0x0e */ 3, // PitchBend Change
+ /* 0x0f */ 1 // Single Byte
+ };
+
+ // Each System MIDI message is a certain size. These can be mapped to a
+ // Code Index number defined in Table 4-1 of USB MIDI 1.0.
+ private static final int[] CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE = new int[]{
+ /* 0x00 */ -1, // Start of Exclusive. Special case.
+ /* 0x01 */ 2, // MIDI Time Code. Two byte message
+ /* 0x02 */ 3, // Song Point Pointer. Three byte message
+ /* 0x03 */ 2, // Song Select. Two byte message
+ /* 0x04 */ -1, // Undefined MIDI System Common
+ /* 0x05 */ -1, // Undefined MIDI System Common
+ /* 0x06 */ 5, // Tune Request. One byte message
+ /* 0x07 */ -1, // End of Exclusive. Special case.
+ /* 0x08 */ 5, // Timing clock. One byte message
+ /* 0x09 */ -1, // Undefined MIDI System Real-time
+ /* 0x0a */ 5, // Start. One byte message
+ /* 0x0b */ 5, // Continue. One byte message
+ /* 0x0c */ 5, // Stop. One byte message
+ /* 0x0d */ -1, // Undefined MIDI System Real-time
+ /* 0x0e */ 5, // Active Sensing. One byte message
+ /* 0x0f */ 5 // System Reset. One byte message
+ };
+
+ // These code index numbers also come from Table 4-1 in USB MIDI 1.0 spec.
+ private static final byte CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES = 0x4;
+ private static final byte CODE_INDEX_NUMBER_SINGLE_BYTE = 0xF;
+ private static final byte CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE = (byte) 0x5;
+
+ // System messages are defined in MIDI.
+ private static final byte FIRST_SYSTEM_MESSAGE_VALUE = (byte) 0xF0;
+ private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0;
+ private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7;
+
+ private UsbMidiEncoder mUsbMidiEncoder = new UsbMidiEncoder();
+ private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder();
+
+ /**
+ * Converts a USB MIDI array into a raw MIDI array.
+ *
+ * @param usbMidiBytes the USB MIDI bytes to convert
+ * @param size the size of usbMidiBytes
+ * @return byte array of raw MIDI packets
+ */
+ public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) {
+ return mUsbMidiDecoder.decode(usbMidiBytes, size);
+ }
+
+ /**
+ * Converts a raw MIDI array into a USB MIDI array.
+ *
+ * @param midiBytes the raw MIDI bytes to convert
+ * @param size the size of usbMidiBytes
+ * @return byte array of USB MIDI packets
+ */
+ public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size) {
+ return mUsbMidiEncoder.encode(midiBytes, size);
+ }
+
+ private class UsbMidiDecoder {
+ // Decodes the data from USB MIDI to raw MIDI.
+ // Each valid 4 byte input maps to a 1-3 byte output.
+ // Reference the USB MIDI 1.0 spec for more info.
+ public byte[] decode(byte[] usbMidiBytes, int size) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ for (int i = 0; i + 3 < size; i += 4) {
+ int codeIndex = usbMidiBytes[i] & 0x0f;
+ int numPayloadBytes = PAYLOAD_SIZE[codeIndex];
+ if (numPayloadBytes < 0) {
+ continue;
+ }
+ outputStream.write(usbMidiBytes, i + 1, numPayloadBytes);
+ }
+ return outputStream.toByteArray();
+ }
+ }
+
+ private class UsbMidiEncoder {
+ // In order to facilitate large scale transfers, SysEx can be sent in multiple packets.
+ // If encode() is called without an SysEx end, we must continue SysEx for the next packet.
+ // All other packets should be 3 bytes or less and must be not be broken between packets.
+ private byte[] mStoredSystemExclusiveBytes = new byte[3];
+ private int mNumStoredSystemExclusiveBytes = 0;
+ private boolean mHasSystemExclusiveStarted = false;
+
+ private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data
+
+ // Encodes the data from raw MIDI to USB MIDI.
+ // Each valid 1-3 byte input maps to a 4 byte output.
+ // Reference the USB MIDI 1.0 spec for more info.
+ // MidiFramer is not needed here as this code handles partial packets.
+ // Long SysEx messages split between packets will encode and return a
+ // byte stream even if the SysEx end has not been sent.
+ // If there are less than 3 remaining data bytes in a SysEx message left,
+ // these bytes will be combined with the next set of packets.
+ public byte[] encode(byte[] midiBytes, int size) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ int curLocation = 0;
+ while (curLocation < size) {
+ if (midiBytes[curLocation] >= 0) { // Data byte
+ if (mHasSystemExclusiveStarted) {
+ mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+ midiBytes[curLocation];
+ mNumStoredSystemExclusiveBytes++;
+ if (mNumStoredSystemExclusiveBytes == 3) {
+ outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES);
+ outputStream.write(mStoredSystemExclusiveBytes, 0, 3);
+ mNumStoredSystemExclusiveBytes = 0;
+ }
+ } else {
+ writeSingleByte(outputStream, midiBytes[curLocation]);
+ }
+ curLocation++;
+ continue;
+ } else if (midiBytes[curLocation] != SYSEX_END_EXCLUSIVE) {
+ // SysEx operation was interrupted. Pass the data directly down.
+ if (mHasSystemExclusiveStarted) {
+ int index = 0;
+ while (index < mNumStoredSystemExclusiveBytes) {
+ writeSingleByte(outputStream, mStoredSystemExclusiveBytes[index]);
+ index++;
+ }
+ mNumStoredSystemExclusiveBytes = 0;
+ mHasSystemExclusiveStarted = false;
+ }
+ }
+
+ if (midiBytes[curLocation] < FIRST_SYSTEM_MESSAGE_VALUE) { // Channel message
+ byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f);
+ int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+ if (curLocation + channelMessageSize <= size) {
+ outputStream.write(codeIndexNumber);
+ outputStream.write(midiBytes, curLocation, channelMessageSize);
+ // Fill in the rest of the bytes with 0.
+ outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize);
+ curLocation += channelMessageSize;
+ } else { // The packet is missing data. Use single byte messages.
+ while (curLocation < size) {
+ writeSingleByte(outputStream, midiBytes[curLocation]);
+ curLocation++;
+ }
+ }
+ } else if (midiBytes[curLocation] == SYSEX_START_EXCLUSIVE) {
+ mHasSystemExclusiveStarted = true;
+ mStoredSystemExclusiveBytes[0] = midiBytes[curLocation];
+ mNumStoredSystemExclusiveBytes = 1;
+ curLocation++;
+ } else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) {
+ // 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07
+ outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE
+ + mNumStoredSystemExclusiveBytes);
+ mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+ midiBytes[curLocation];
+ mNumStoredSystemExclusiveBytes++;
+ outputStream.write(mStoredSystemExclusiveBytes, 0,
+ mNumStoredSystemExclusiveBytes);
+ // Fill in the rest of the bytes with 0.
+ outputStream.write(mEmptyBytes, 0, 3 - mNumStoredSystemExclusiveBytes);
+ mHasSystemExclusiveStarted = false;
+ mNumStoredSystemExclusiveBytes = 0;
+ curLocation++;
+ } else {
+ int systemType = midiBytes[curLocation] & 0x0f;
+ int codeIndexNumber = CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE[systemType];
+ if (codeIndexNumber < 0) { // Unknown type. Use single byte messages.
+ writeSingleByte(outputStream, midiBytes[curLocation]);
+ curLocation++;
+ } else {
+ int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+ if (curLocation + systemMessageSize <= size) {
+ outputStream.write(codeIndexNumber);
+ outputStream.write(midiBytes, curLocation, systemMessageSize);
+ // Fill in the rest of the bytes with 0.
+ outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize);
+ curLocation += systemMessageSize;
+ } else { // The packet is missing data. Use single byte messages.
+ while (curLocation < size) {
+ writeSingleByte(outputStream, midiBytes[curLocation]);
+ curLocation++;
+ }
+ }
+ }
+ }
+ }
+ return outputStream.toByteArray();
+ }
+
+ private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) {
+ outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE);
+ outputStream.write(byteToWrite);
+ outputStream.write(0);
+ outputStream.write(0);
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6e68a91..cd6ea68 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -198,14 +198,20 @@
if (mCurInterfaceDescriptor != null) {
switch (mCurInterfaceDescriptor.getUsbClass()) {
case UsbDescriptor.CLASSID_AUDIO:
- descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+ descriptor =
+ UsbACInterface.allocDescriptor(this, stream, length, type);
+ if (descriptor instanceof UsbMSMidiHeader) {
+ mCurInterfaceDescriptor.setMidiHeaderInterfaceDescriptor(
+ descriptor);
+ }
break;
case UsbDescriptor.CLASSID_VIDEO:
if (DEBUG) {
Log.d(TAG, " UsbDescriptor.CLASSID_VIDEO");
}
- descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+ descriptor =
+ UsbVCInterface.allocDescriptor(this, stream, length, type);
break;
case UsbDescriptor.CLASSID_AUDIOVIDEO:
@@ -218,7 +224,6 @@
Log.w(TAG, " Unparsed Class-specific");
break;
}
- mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor);
}
break;
@@ -674,6 +679,23 @@
public boolean containsUniversalMidiDeviceEndpoint() {
ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
findUniversalMidiInterfaceDescriptors();
+ return doesInterfaceContainEndpoint(interfaceDescriptors);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean containsLegacyMidiDeviceEndpoint() {
+ ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
+ findLegacyMidiInterfaceDescriptors();
+ return doesInterfaceContainEndpoint(interfaceDescriptors);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean doesInterfaceContainEndpoint(
+ ArrayList<UsbInterfaceDescriptor> interfaceDescriptors) {
int outputCount = 0;
int inputCount = 0;
for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size();
@@ -698,10 +720,24 @@
* @hide
*/
public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() {
+ return findMidiInterfaceDescriptors(MS_MIDI_2_0);
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbInterfaceDescriptor> findLegacyMidiInterfaceDescriptors() {
+ return findMidiInterfaceDescriptors(MS_MIDI_1_0);
+ }
+
+ /**
+ * @hide
+ */
+ private ArrayList<UsbInterfaceDescriptor> findMidiInterfaceDescriptors(int type) {
int count = 0;
ArrayList<UsbDescriptor> descriptors =
getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
- ArrayList<UsbInterfaceDescriptor> universalMidiInterfaces =
+ ArrayList<UsbInterfaceDescriptor> midiInterfaces =
new ArrayList<UsbInterfaceDescriptor>();
for (UsbDescriptor descriptor : descriptors) {
@@ -709,14 +745,14 @@
if (descriptor instanceof UsbInterfaceDescriptor) {
UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
- UsbDescriptor classSpecificDescriptor =
- interfaceDescriptor.getClassSpecificInterfaceDescriptor();
- if (classSpecificDescriptor != null) {
- if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+ UsbDescriptor midiHeaderDescriptor =
+ interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+ if (midiHeaderDescriptor != null) {
+ if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
UsbMSMidiHeader midiHeader =
- (UsbMSMidiHeader) classSpecificDescriptor;
- if (midiHeader.getMidiStreamingClass() == MS_MIDI_2_0) {
- universalMidiInterfaces.add(interfaceDescriptor);
+ (UsbMSMidiHeader) midiHeaderDescriptor;
+ if (midiHeader.getMidiStreamingClass() == type) {
+ midiInterfaces.add(interfaceDescriptor);
}
}
}
@@ -726,10 +762,13 @@
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
- return universalMidiInterfaces;
+ return midiInterfaces;
}
- private int calculateNumLegacyMidiPorts(boolean isOutput) {
+ /**
+ * @hide
+ */
+ public int calculateMidiInterfaceDescriptorsCount() {
int count = 0;
ArrayList<UsbDescriptor> descriptors =
getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
@@ -738,22 +777,12 @@
if (descriptor instanceof UsbInterfaceDescriptor) {
UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
- UsbDescriptor classSpecificDescriptor =
- interfaceDescriptor.getClassSpecificInterfaceDescriptor();
- if (classSpecificDescriptor != null) {
- if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+ UsbDescriptor midiHeaderDescriptor =
+ interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+ if (midiHeaderDescriptor != null) {
+ if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
UsbMSMidiHeader midiHeader =
- (UsbMSMidiHeader) classSpecificDescriptor;
- if (midiHeader.getMidiStreamingClass() != MS_MIDI_1_0) {
- continue;
- }
- }
- }
- for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
- UsbEndpointDescriptor endpoint =
- interfaceDescriptor.getEndpointDescriptor(i);
- // 0 is output, 1 << 7 is input.
- if ((endpoint.getDirection() == 0) == isOutput) {
+ (UsbMSMidiHeader) midiHeaderDescriptor;
count++;
}
}
@@ -769,20 +798,6 @@
/**
* @hide
*/
- public int calculateNumLegacyMidiInputs() {
- return calculateNumLegacyMidiPorts(false /*isOutput*/);
- }
-
- /**
- * @hide
- */
- public int calculateNumLegacyMidiOutputs() {
- return calculateNumLegacyMidiPorts(true /*isOutput*/);
- }
-
- /**
- * @hide
- */
public float getInputHeadsetProbability() {
if (hasMIDIInterface()) {
return 0.0f;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index ca4613b..9ddcb10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -42,7 +42,8 @@
private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
new ArrayList<UsbEndpointDescriptor>();
- private UsbDescriptor mClassSpecificInterfaceDescriptor;
+ // Used for MIDI only.
+ private UsbDescriptor mMidiHeaderInterfaceDescriptor;
UsbInterfaceDescriptor(int length, byte type) {
super(length, type);
@@ -107,12 +108,12 @@
mEndpointDescriptors.add(endpoint);
}
- public void setClassSpecificInterfaceDescriptor(UsbDescriptor descriptor) {
- mClassSpecificInterfaceDescriptor = descriptor;
+ public void setMidiHeaderInterfaceDescriptor(UsbDescriptor descriptor) {
+ mMidiHeaderInterfaceDescriptor = descriptor;
}
- public UsbDescriptor getClassSpecificInterfaceDescriptor() {
- return mClassSpecificInterfaceDescriptor;
+ public UsbDescriptor getMidiHeaderInterfaceDescriptor() {
+ return mMidiHeaderInterfaceDescriptor;
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f57c32c..ad04952 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7664,7 +7664,7 @@
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* TODO: remove this one. use {@link #rebootModem()} for reset type 1 and
- * {@link #resetRadioConfig()} for reset type 3
+ * {@link #resetRadioConfig()} for reset type 3 (b/116476729)
*
* @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
* @return true on success; false on any failure.
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 3415868..783c0d1 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -497,6 +497,8 @@
try {
return imsRcsController.isCapable(mSubId, capability, radioTech);
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException e) {
Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
throw new ImsException("Remote IMS Service is not available",
@@ -534,6 +536,8 @@
try {
return imsRcsController.isAvailable(mSubId, capability, radioTech);
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException e) {
Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
throw new ImsException("Remote IMS Service is not available",
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index c59a41e..9a9e42b 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,8 +37,6 @@
"vts",
],
data: [
- ":NotoSerif-Regular.ttf",
- ":NotoSerif-Bold.ttf",
":UpdatableSystemFontTestCertDer",
":UpdatableSystemFontTest_NotoColorEmoji.ttf",
":UpdatableSystemFontTest_NotoColorEmoji.sig",
@@ -48,7 +46,9 @@
":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Regular.ttf",
":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Bold.ttf",
":UpdatableSystemFontTest_NotoSerif-Bold.sig",
],
sdk_version: "test_current",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 6effa7b..9e2a4b6 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -28,11 +28,7 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
- <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
- <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
- <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
- <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
- <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf" />
<option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
<option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
<option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
@@ -40,6 +36,10 @@
<option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
<option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
<option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 87fda0d..cbe13d9 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -84,7 +84,7 @@
private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
private static final String NOTO_COLOR_EMOJI_TTF =
- "/data/local/tmp/NotoColorEmoji.ttf";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf";
private static final String NOTO_COLOR_EMOJI_SIG =
"/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
// A font with revision == 0.
@@ -105,13 +105,13 @@
private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
private static final String NOTO_SERIF_REGULAR_TTF =
- "/data/local/tmp/NotoSerif-Regular.ttf";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf";
private static final String NOTO_SERIF_REGULAR_SIG =
"/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
private static final String NOTO_SERIF_BOLD_TTF =
- "/data/local/tmp/NotoSerif-Bold.ttf";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf";
private static final String NOTO_SERIF_BOLD_SIG =
"/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 64b698d..0bdb3a8 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -21,11 +21,19 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-// An existing module name is reused to avoid merge conflicts.
-// TODO: fix the font file name.
filegroup {
name: "UpdatableSystemFontTest_NotoColorEmoji.ttf",
- srcs: ["NotoColorEmoji.ttf"],
+ srcs: ["UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTest_NotoSerif-Regular.ttf",
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTest_NotoSerif-Bold.ttf",
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
}
filegroup {
@@ -43,10 +51,6 @@
srcs: ["UpdatableSystemFontTestCert.der"],
}
-genrule_defaults {
- name: "updatable_system_font_increment_font_revision_default",
-}
-
genrule {
name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
@@ -124,13 +128,13 @@
genrule {
name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":NotoSerif-Regular.ttf"],
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
}
genrule {
name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":NotoSerif-Bold.ttf"],
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
}
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
new file mode 100644
index 0000000..66c1bd2
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
new file mode 100644
index 0000000..8c4215e
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <!-- Currently NotoSerif-Bold.ttf's fontRevision is 1.xx.
+ 100.0 will be sufficiently larger than that. -->
+ <fontRevision value="100.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Feb 16 12:00:00 2022"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="3000" lsb="93"/> <!-- 3em -->
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <!-- length will be calculated by the compiler. -->
+ <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+ <!-- The font must support at least one of the characters used
+ in OtfFontFileParser to validate the font. -->
+ <map code="0x61" name="a" />
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="0" y="300" on="1" />
+ <pt x="300" y="300" on="1" />
+ <pt x="300" y="0" on="1" />
+ </contour>
+ <instructions />
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2022 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Bold
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ <!-- Android identifies the target font to be updated by PostScript name.
+ To test updating NotoSerif-Bold.ttf, the PostScript needs to be
+ the same as NotoSerif-Bold.ttf here. -->
+ NotoSerif-Bold
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
new file mode 100644
index 0000000..707ae28
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
new file mode 100644
index 0000000..754eae3
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <!-- Currently NotoSerif-Regular.ttf's fontRevision is 1.xx.
+ 100.0 will be sufficiently larger than that. -->
+ <fontRevision value="100.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Feb 16 12:00:00 2022"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="3000" lsb="93"/> <!-- 3em -->
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <!-- length will be calculated by the compiler. -->
+ <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+ <!-- The font must support at least one of the characters used
+ in OtfFontFileParser to validate the font. -->
+ <map code="0x61" name="a" />
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="0" y="300" on="1" />
+ <pt x="300" y="300" on="1" />
+ <pt x="300" y="0" on="1" />
+ </contour>
+ <instructions />
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2022 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ <!-- Android identifies the target font to be updated by PostScript name.
+ To test updating NotoSerif-Regular.ttf, the PostScript needs to be
+ the same as NotoSerif-Regular.ttf here. -->
+ NotoSerif
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bd0a4bc..52c5d48 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -36,6 +36,7 @@
cc_defaults {
name: "aapt2_defaults",
+ cpp_std: "gnu++2b",
cflags: [
"-Wall",
"-Werror",
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 7103944..f47d66e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -80,7 +80,7 @@
printer_->Print(parent_name.package);
printer_->Print(":");
}
- printer_->Print(to_string(parent_name.type));
+ printer_->Print(parent_name.type.to_string());
printer_->Print("/");
printer_->Print(parent_name.entry);
if (parent_ref.id) {
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 6364ccd..0bb330e 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -134,6 +134,24 @@
{"xml", ResourceType::kXml},
};
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t) {
+ return {to_string(t), t};
+}
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s) {
+ auto colon = std::find(s.begin(), s.end(), ':');
+ const ResourceType* parsedType;
+ if (colon != s.end() && colon != std::prev(s.end())) {
+ parsedType = ParseResourceType(s.substr(s.begin(), colon));
+ } else {
+ parsedType = ParseResourceType(s);
+ }
+ if (parsedType == nullptr) {
+ return std::nullopt;
+ }
+ return ResourceNamedTypeRef(s, *parsedType);
+}
+
const ResourceType* ParseResourceType(const StringPiece& str) {
auto iter = sResourceTypeMap.find(str);
if (iter == std::end(sResourceTypeMap)) {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 307c21d..b41d851 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -19,22 +19,21 @@
#include <iomanip>
#include <limits>
+#include <optional>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>
+#include "Source.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/StringPiece.h"
#include "utils/JenkinsHash.h"
-#include "Source.h"
-
namespace aapt {
/**
- * The various types of resource types available. Corresponds
- * to the 'type' in package:type/entry.
+ * The various types of resource types available.
*/
enum class ResourceType {
kAnim,
@@ -78,15 +77,63 @@
const ResourceType* ParseResourceType(const android::StringPiece& str);
/**
+ * Pair of type name as in ResourceTable and actual resource type.
+ * Corresponds to the 'type' in package:type/entry.
+ *
+ * This is to support resource types with custom names inside resource tables.
+ */
+struct ResourceNamedType {
+ std::string name;
+ ResourceType type = ResourceType::kRaw;
+
+ ResourceNamedType() = default;
+ ResourceNamedType(const android::StringPiece& n, ResourceType t);
+
+ int compare(const ResourceNamedType& other) const;
+
+ const std::string& to_string() const;
+};
+
+/**
+ * Same as ResourceNamedType, but uses StringPieces instead.
+ * Use this if you need to avoid copying and know that
+ * the lifetime of this object is shorter than that
+ * of the original string.
+ */
+struct ResourceNamedTypeRef {
+ android::StringPiece name;
+ ResourceType type = ResourceType::kRaw;
+
+ ResourceNamedTypeRef() = default;
+ ResourceNamedTypeRef(const ResourceNamedTypeRef&) = default;
+ ResourceNamedTypeRef(ResourceNamedTypeRef&&) = default;
+ ResourceNamedTypeRef(const ResourceNamedType& rhs); // NOLINT(google-explicit-constructor)
+ ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t);
+ ResourceNamedTypeRef& operator=(const ResourceNamedTypeRef& rhs) = default;
+ ResourceNamedTypeRef& operator=(ResourceNamedTypeRef&& rhs) = default;
+ ResourceNamedTypeRef& operator=(const ResourceNamedType& rhs);
+
+ ResourceNamedType ToResourceNamedType() const;
+
+ std::string to_string() const;
+};
+
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t);
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s);
+
+/**
* A resource's name. This can uniquely identify
* a resource in the ResourceTable.
*/
struct ResourceName {
std::string package;
- ResourceType type = ResourceType::kRaw;
+ ResourceNamedType type;
std::string entry;
ResourceName() = default;
+ ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+ const android::StringPiece& e);
ResourceName(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
int compare(const ResourceName& other) const;
@@ -103,13 +150,15 @@
*/
struct ResourceNameRef {
android::StringPiece package;
- ResourceType type = ResourceType::kRaw;
+ ResourceNamedTypeRef type;
android::StringPiece entry;
ResourceNameRef() = default;
ResourceNameRef(const ResourceNameRef&) = default;
ResourceNameRef(ResourceNameRef&&) = default;
ResourceNameRef(const ResourceName& rhs); // NOLINT(google-explicit-constructor)
+ ResourceNameRef(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+ const android::StringPiece& e);
ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
@@ -295,17 +344,98 @@
}
//
+// ResourceNamedType implementation.
+//
+inline ResourceNamedType::ResourceNamedType(const android::StringPiece& n, ResourceType t)
+ : name(n.to_string()), type(t) {
+}
+
+inline int ResourceNamedType::compare(const ResourceNamedType& other) const {
+ int cmp = static_cast<int>(type) - static_cast<int>(other.type);
+ if (cmp != 0) return cmp;
+ cmp = name.compare(other.name);
+ return cmp;
+}
+
+inline const std::string& ResourceNamedType::to_string() const {
+ return name;
+}
+
+inline bool operator<(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+ return lhs.compare(rhs) < 0;
+}
+
+inline bool operator==(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+ return lhs.compare(rhs) == 0;
+}
+
+inline bool operator!=(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+ return lhs.compare(rhs) != 0;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedType& val) {
+ return out << val.to_string();
+}
+
+//
+// ResourceNamedTypeRef implementation.
+//
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t)
+ : name(n), type(t) {
+}
+
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const ResourceNamedType& rhs)
+ : name(rhs.name), type(rhs.type) {
+}
+
+inline ResourceNamedTypeRef& ResourceNamedTypeRef::operator=(const ResourceNamedType& rhs) {
+ name = rhs.name;
+ type = rhs.type;
+ return *this;
+}
+
+inline ResourceNamedType ResourceNamedTypeRef::ToResourceNamedType() const {
+ return ResourceNamedType(name, type);
+}
+
+inline std::string ResourceNamedTypeRef::to_string() const {
+ return name.to_string();
+}
+
+inline bool operator<(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+ return std::tie(lhs.type, lhs.name) < std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator==(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+ return std::tie(lhs.type, lhs.name) == std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator!=(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+ return std::tie(lhs.type, lhs.name) != std::tie(rhs.type, rhs.name);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedTypeRef& val) {
+ return out << val.name;
+}
+
+//
// ResourceName implementation.
//
+inline ResourceName::ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+ const android::StringPiece& e)
+ : package(p.to_string()), type(t.ToResourceNamedType()), entry(e.to_string()) {
+}
+
inline ResourceName::ResourceName(const android::StringPiece& p, ResourceType t,
const android::StringPiece& e)
- : package(p.to_string()), type(t), entry(e.to_string()) {}
+ : ResourceName(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
inline int ResourceName::compare(const ResourceName& other) const {
int cmp = package.compare(other.package);
if (cmp != 0) return cmp;
- cmp = static_cast<int>(type) - static_cast<int>(other.type);
+ cmp = type.compare(other.type);
if (cmp != 0) return cmp;
cmp = entry.compare(other.entry);
return cmp;
@@ -341,9 +471,16 @@
inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs)
: package(rhs.package), type(rhs.type), entry(rhs.entry) {}
+inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p,
+ const ResourceNamedTypeRef& t,
+ const android::StringPiece& e)
+ : package(p), type(t), entry(e) {
+}
+
inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p, ResourceType t,
const android::StringPiece& e)
- : package(p), type(t), entry(e) {}
+ : ResourceNameRef(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
package = rhs.package;
@@ -400,7 +537,7 @@
size_t operator()(const aapt::ResourceName& name) const {
android::hash_t h = 0;
h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.package)));
- h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
+ h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.type.name)));
h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.entry)));
return static_cast<size_t>(h);
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 792a306..42715f9 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -604,7 +604,8 @@
return false;
}
- out_resource->name.type = ResourceType::kId;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kId).ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
// Ids either represent a unique resource id or reference another resource id
@@ -623,7 +624,7 @@
// A null reference also means there is no inner element when ids are in the form:
// <id name="name"/>
out_resource->value = util::make_unique<Id>();
- } else if (!ref || ref->name.value().type != ResourceType::kId) {
+ } else if (!ref || ref->name.value().type.type != ResourceType::kId) {
// If an inner element exists, the inner element must be a reference to another resource id
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name()
@@ -640,7 +641,8 @@
return false;
}
- out_resource->name.type = ResourceType::kMacro;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kMacro).ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
return ParseMacro(parser, out_resource);
}
@@ -656,7 +658,8 @@
return false;
}
- out_resource->name.type = item_iter->second.type;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(item_iter->second.type).ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
// Only use the implied format of the type when there is no explicit format.
@@ -699,7 +702,7 @@
if (can_be_item) {
// Try parsing the elementName (or type) as a resource. These shall only be
// resources like 'layout' or 'xml' and they can only be references.
- const ResourceType* parsed_type = ParseResourceType(resource_type);
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(resource_type);
if (parsed_type) {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
@@ -708,7 +711,7 @@
return false;
}
- out_resource->name.type = *parsed_type;
+ out_resource->name.type = parsed_type->ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
if (!out_resource->value) {
@@ -933,7 +936,7 @@
return false;
}
- const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
if (!parsed_type) {
diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
<< maybe_type.value()
@@ -941,7 +944,7 @@
return false;
}
- out_resource->name.type = *parsed_type;
+ out_resource->name.type = parsed_type->ToResourceNamedType();
if (std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
@@ -953,7 +956,7 @@
out_resource->id = maybe_id.value();
}
- if (*parsed_type == ResourceType::kId) {
+ if (parsed_type->type == ResourceType::kId) {
// An ID marked as public is also the definition of an ID.
out_resource->value = util::make_unique<Id>();
}
@@ -978,7 +981,7 @@
return false;
}
- const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
if (!parsed_type) {
diag->Error(DiagMessage(out_resource->source)
<< "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
@@ -1096,7 +1099,7 @@
return false;
}
- const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
if (!parsed_type) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid resource type '" << maybe_type.value() << "' in <"
@@ -1104,7 +1107,7 @@
return false;
}
- out_resource->name.type = *parsed_type;
+ out_resource->name.type = parsed_type->ToResourceNamedType();
return true;
}
@@ -1208,8 +1211,8 @@
continue;
}
- const ResourceType* type = ParseResourceType(item_type.value());
- if (type == nullptr) {
+ std::optional<ResourceNamedTypeRef> type = ParseResourceNamedType(item_type.value());
+ if (!type) {
diag_->Error(DiagMessage(element_source)
<< "invalid resource type '" << item_type.value()
<< "' in <item> within an <overlayable>");
@@ -1223,7 +1226,7 @@
overlayable_item.source = element_source;
ParsedResource child_resource{};
- child_resource.name.type = *type;
+ child_resource.name.type = type->ToResourceNamedType();
child_resource.name.entry = item_name.value().to_string();
child_resource.overlayable_item = overlayable_item;
out_resource->child_resources.push_back(std::move(child_resource));
@@ -1289,7 +1292,8 @@
bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource, bool weak) {
- out_resource->name.type = ResourceType::kAttr;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kAttr).ToResourceNamedType();
// Attributes only end up in default configuration.
if (out_resource->config != ConfigDescription::DefaultConfig()) {
@@ -1475,7 +1479,8 @@
}
return Attribute::Symbol{
- Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
+ Reference(ResourceNameRef({}, ResourceNamedTypeWithDefaultName(ResourceType::kId),
+ maybe_name.value())),
val.data, val.dataType};
}
@@ -1509,7 +1514,7 @@
bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- out_resource->name.type = type;
+ out_resource->name.type = ResourceNamedTypeWithDefaultName(type).ToResourceNamedType();
std::unique_ptr<Style> style = util::make_unique<Style>();
@@ -1535,7 +1540,8 @@
size_t pos = style_name.find_last_of(u'.');
if (pos != std::string::npos) {
style->parent_inferred = true;
- style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
+ style->parent = Reference(ResourceName(
+ {}, ResourceNamedTypeWithDefaultName(ResourceType::kStyle), style_name.substr(0, pos)));
}
}
@@ -1591,7 +1597,8 @@
bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource,
const uint32_t typeMask) {
- out_resource->name.type = ResourceType::kArray;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kArray).ToResourceNamedType();
std::unique_ptr<Array> array = util::make_unique<Array>();
@@ -1646,7 +1653,8 @@
bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- out_resource->name.type = ResourceType::kPlurals;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kPlurals).ToResourceNamedType();
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
@@ -1727,7 +1735,8 @@
bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- out_resource->name.type = ResourceType::kStyleable;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kStyleable).ToResourceNamedType();
if (!options_.preserve_visibility_of_styleables) {
// This was added in change Idd21b5de4d20be06c6f8c8eb5a22ccd68afc4927 to mimic aapt1, but no one
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index dae89b0..98cce26 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -473,7 +473,7 @@
}
auto package = FindOrCreatePackage(res.name.package);
- auto type = package->FindOrCreateType(res.name.type);
+ auto type = package->FindOrCreateType(res.name.type.type);
auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), res.name.entry,
NameEqualRange<ResourceEntry>{});
const size_t entry_count = std::distance(entry_it.first, entry_it.second);
@@ -593,7 +593,7 @@
return {};
}
- ResourceTableType* type = package->FindType(name.type);
+ ResourceTableType* type = package->FindType(name.type.type);
if (type == nullptr) {
return {};
}
@@ -612,7 +612,7 @@
return {};
}
- ResourceTableType* type = package->FindType(name.type);
+ ResourceTableType* type = package->FindType(name.type.type);
if (type == nullptr) {
return {};
}
@@ -633,7 +633,7 @@
return {};
}
- ResourceTableType* type = package->FindType(name.type);
+ ResourceTableType* type = package->FindType(name.type.type);
if (type == nullptr) {
return {};
}
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ead06bf..23f6c88 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -50,12 +50,11 @@
name_out.package =
util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
- const ResourceType* type;
+ std::optional<ResourceNamedTypeRef> type;
if (name_in.type) {
- type = ParseResourceType(
- util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
+ type = ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
} else if (name_in.type8) {
- type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
+ type = ParseResourceNamedType(StringPiece(name_in.type8, name_in.typeLen));
} else {
return {};
}
@@ -64,7 +63,7 @@
return {};
}
- name_out.type = *type;
+ name_out.type = type->ToResourceNamedType();
if (name_in.name) {
name_out.entry =
@@ -85,12 +84,12 @@
name_out.package = std::string(name_in.package, name_in.package_len);
- const ResourceType* type;
+ std::optional<ResourceNamedTypeRef> type;
if (name_in.type16) {
- type = ParseResourceType(
- util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
+ type =
+ ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
} else if (name_in.type) {
- type = ParseResourceType(StringPiece(name_in.type, name_in.type_len));
+ type = ParseResourceNamedType(StringPiece(name_in.type, name_in.type_len));
} else {
return {};
}
@@ -99,7 +98,7 @@
return {};
}
- name_out.type = *type;
+ name_out.type = type->ToResourceNamedType();
if (name_in.entry16) {
name_out.entry =
@@ -133,7 +132,7 @@
return false;
}
- const ResourceType* parsed_type = ParseResourceType(type);
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(type);
if (!parsed_type) {
return false;
}
@@ -181,7 +180,7 @@
return false;
}
- if (create && name.type != ResourceType::kId) {
+ if (create && name.type.type != ResourceType::kId) {
return false;
}
@@ -230,7 +229,7 @@
if (out_ref) {
out_ref->package = package;
- out_ref->type = ResourceType::kAttr;
+ out_ref->type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
out_ref->entry = entry;
}
return true;
@@ -272,7 +271,7 @@
}
ResourceNameRef ref;
- ref.type = ResourceType::kStyle;
+ ref.type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
StringPiece type_str;
android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
@@ -323,7 +322,8 @@
p++;
}
- ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
+ ref.name = ResourceName(package, ResourceNamedTypeWithDefaultName(ResourceType::kAttr),
+ name.empty() ? trimmed_str : name);
return std::optional<Reference>(std::move(ref));
}
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b3ab4ff..b796eb0 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -116,7 +116,7 @@
}
bool Reference::Flatten(android::Res_value* out_value) const {
- if (name && name.value().type == ResourceType::kMacro) {
+ if (name && name.value().type.type == ResourceType::kMacro) {
return false;
}
@@ -192,7 +192,7 @@
if (print_package) {
printer->Print(name.to_string());
} else {
- printer->Print(to_string(name.type));
+ printer->Print(name.type.to_string());
printer->Print("/");
printer->Print(name.entry);
}
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index c557f3c..2c55d1d 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -18,6 +18,9 @@
#include "test/Test.h"
+using ::testing::Eq;
+using ::testing::Optional;
+
namespace aapt {
TEST(ResourceTypeTest, ParseResourceTypes) {
@@ -125,4 +128,104 @@
EXPECT_EQ(type, nullptr);
}
+TEST(ResourceTypeTest, ParseResourceNamedType) {
+ auto type = ParseResourceNamedType("anim");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("anim", ResourceType::kAnim))));
+
+ type = ParseResourceNamedType("layout");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout", ResourceType::kLayout))));
+
+ type = ParseResourceNamedType("layout:2");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:2", ResourceType::kLayout))));
+
+ type = ParseResourceNamedType("layout:another");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:another", ResourceType::kLayout))));
+
+ type = ParseResourceNamedType("layout:");
+ EXPECT_THAT(type, Eq(std::nullopt));
+
+ type = ParseResourceNamedType("layout2");
+ EXPECT_THAT(type, Eq(std::nullopt));
+
+ type = ParseResourceNamedType("blahaha");
+ EXPECT_THAT(type, Eq(std::nullopt));
+}
+
+TEST(ResourceTypeTest, ResourceNamedTypeWithDefaultName) {
+ auto type = ResourceNamedTypeWithDefaultName(ResourceType::kAnim);
+ EXPECT_THAT(type, Eq(ResourceNamedType("anim", ResourceType::kAnim)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kAnimator);
+ EXPECT_THAT(type, Eq(ResourceNamedType("animator", ResourceType::kAnimator)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kArray);
+ EXPECT_THAT(type, Eq(ResourceNamedType("array", ResourceType::kArray)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
+ EXPECT_THAT(type, Eq(ResourceNamedType("attr", ResourceType::kAttr)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kAttrPrivate);
+ EXPECT_THAT(type, Eq(ResourceNamedType("^attr-private", ResourceType::kAttrPrivate)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kBool);
+ EXPECT_THAT(type, Eq(ResourceNamedType("bool", ResourceType::kBool)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kColor);
+ EXPECT_THAT(type, Eq(ResourceNamedType("color", ResourceType::kColor)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kConfigVarying);
+ EXPECT_THAT(type, Eq(ResourceNamedType("configVarying", ResourceType::kConfigVarying)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kDimen);
+ EXPECT_THAT(type, Eq(ResourceNamedType("dimen", ResourceType::kDimen)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kDrawable);
+ EXPECT_THAT(type, Eq(ResourceNamedType("drawable", ResourceType::kDrawable)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kFont);
+ EXPECT_THAT(type, Eq(ResourceNamedType("font", ResourceType::kFont)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kFraction);
+ EXPECT_THAT(type, Eq(ResourceNamedType("fraction", ResourceType::kFraction)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kId);
+ EXPECT_THAT(type, Eq(ResourceNamedType("id", ResourceType::kId)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kInteger);
+ EXPECT_THAT(type, Eq(ResourceNamedType("integer", ResourceType::kInteger)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kInterpolator);
+ EXPECT_THAT(type, Eq(ResourceNamedType("interpolator", ResourceType::kInterpolator)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kLayout);
+ EXPECT_THAT(type, Eq(ResourceNamedType("layout", ResourceType::kLayout)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kMenu);
+ EXPECT_THAT(type, Eq(ResourceNamedType("menu", ResourceType::kMenu)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kMipmap);
+ EXPECT_THAT(type, Eq(ResourceNamedType("mipmap", ResourceType::kMipmap)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kNavigation);
+ EXPECT_THAT(type, Eq(ResourceNamedType("navigation", ResourceType::kNavigation)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kPlurals);
+ EXPECT_THAT(type, Eq(ResourceNamedType("plurals", ResourceType::kPlurals)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kRaw);
+ EXPECT_THAT(type, Eq(ResourceNamedType("raw", ResourceType::kRaw)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kString);
+ EXPECT_THAT(type, Eq(ResourceNamedType("string", ResourceType::kString)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
+ EXPECT_THAT(type, Eq(ResourceNamedType("style", ResourceType::kStyle)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kTransition);
+ EXPECT_THAT(type, Eq(ResourceNamedType("transition", ResourceType::kTransition)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kXml);
+ EXPECT_THAT(type, Eq(ResourceNamedType("xml", ResourceType::kXml)));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e614a75..790f2b3 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -207,7 +207,7 @@
}
// Check to see if this is an 'id' with the target package.
- if (name.type == ResourceType::kId && symbol->id) {
+ if (name.type.type == ResourceType::kId && symbol->id) {
ResourceId* id = &symbol->id.value();
if (id->package_id() > kAppPackageId) {
// Rewrite the resource ID to be compatible pre-O.
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 339b8af..fa816be 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -275,7 +275,7 @@
return false;
}
- auto key = ResourceTypeKey{name.type, id.type_id()};
+ auto key = ResourceTypeKey{name.type.type, id.type_id()};
auto type = types_.find(key);
if (type == types_.end()) {
// The type has not been assigned an id yet. Ensure that the specified id is not being used by
@@ -291,7 +291,7 @@
if (!visibility.staged_api) {
// Ensure that non-staged resources can only exist in one type ID.
- auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+ auto non_staged_type = non_staged_type_ids_.emplace(name.type.type, id.type_id());
if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
<< " because type already has ID " << std::hex
@@ -316,14 +316,14 @@
CHECK(name.package.empty() || name.package == package_name_);
// Find the type id for non-staged resources of this type.
- auto non_staged_type = non_staged_type_ids_.find(name.type);
+ auto non_staged_type = non_staged_type_ids_.find(name.type.type);
if (non_staged_type == non_staged_type_ids_.end()) {
auto next_type_id = type_id_finder_.NextId();
CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
- non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+ non_staged_type = non_staged_type_ids_.emplace(name.type.type, *next_type_id).first;
}
- ResourceTypeKey key{name.type, non_staged_type->second};
+ ResourceTypeKey key{name.type.type, non_staged_type->second};
auto type = types_.find(key);
if (type == types_.end()) {
type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index 5054115..bb72159 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -46,7 +46,7 @@
ResourceNameRef name;
bool create = false;
if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) {
- if (create && name.type == ResourceType::kId) {
+ if (create && name.type.type == ResourceType::kId) {
if (!text::IsValidResourceEntryName(name.entry)) {
source_diag_->Error(DiagMessage(element->line_number)
<< "id '" << name << "' has an invalid entry name");
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 11d02f0..c65c550 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -556,8 +556,8 @@
std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
const ConfigDescription& config,
const android::Res_value& value) {
- std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_,
- value, &table_->string_pool);
+ std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(
+ name.type.type, config, value_pool_, value, &table_->string_pool);
if (files_ != nullptr) {
FileReference* file_ref = ValueCast<FileReference>(item.get());
if (file_ref != nullptr) {
@@ -575,7 +575,7 @@
std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef& name,
const ConfigDescription& config,
const ResTable_map_entry* map) {
- switch (name.type) {
+ switch (name.type.type) {
case ResourceType::kStyle:
// fallthrough
case ResourceType::kConfigVarying: // legacy thing used in tests
@@ -594,8 +594,8 @@
// We can ignore the value here.
return util::make_unique<Id>();
default:
- diag_->Error(DiagMessage() << "illegal map type '" << to_string(name.type) << "' ("
- << (int)name.type << ")");
+ diag_->Error(DiagMessage() << "illegal map type '" << name.type << "' ("
+ << (int)name.type.type << ")");
break;
}
return {};
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 3b3c6e1..a963d98 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -229,8 +229,8 @@
const std::string package_name =
name.package.empty() ? fallback_package_name.to_string() : name.package;
const std::string entry = JavaClassGenerator::TransformToFieldName(name.entry);
- return FieldReference(
- StringPrintf("%s.R.%s.%s", package_name.c_str(), to_string(name.type).data(), entry.c_str()));
+ return FieldReference(StringPrintf("%s.R.%s.%s", package_name.c_str(),
+ name.type.to_string().data(), entry.c_str()));
}
bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
@@ -451,7 +451,7 @@
MethodDefinition* out_rewrite_method,
text::Printer* r_txt_printer) {
ResourceId real_id = id;
- if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
+ if (context_->GetMinSdkVersion() < SDK_O && name.type.type == ResourceType::kId &&
id.package_id() > kAppPackageId) {
// Workaround for feature splits using package IDs > 0x7F.
// See b/37498913.
@@ -489,7 +489,7 @@
if (r_txt_printer != nullptr) {
r_txt_printer->Print("int ")
- .Print(to_string(name.type))
+ .Print(name.type.to_string())
.Print(" ")
.Print(field_name)
.Print(" ")
@@ -497,7 +497,7 @@
}
if (out_rewrite_method != nullptr) {
- const StringPiece& type_str = to_string(name.type);
+ const std::string type_str = name.type.to_string();
out_rewrite_method->AppendStatement(
StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | packageIdBits;", type_str.data(),
field_name.data(), type_str.data(), field_name.data()));
@@ -561,7 +561,7 @@
return false;
}
- if (resource_name.type == ResourceType::kStyleable) {
+ if (resource_name.type.type == ResourceType::kStyleable) {
CHECK(!entry->values.empty());
const auto styleable = reinterpret_cast<const Styleable*>(entry->values.front()->value.get());
if (!ProcessStyleable(resource_name, id, *styleable, package_name_to_generate,
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 4a2d0ae..e53e220 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -358,7 +358,7 @@
return false;
}
- switch (res->file.name.type) {
+ switch (res->file.name.type.type) {
case ResourceType::kLayout: {
LayoutVisitor visitor(res->file, keep_set);
res->root->Accept(&visitor);
@@ -465,7 +465,7 @@
locations->insert(location);
// TODO: allow for more reference types if we can determine its safe.
- if (location.name.type != ResourceType::kLayout) {
+ if (location.name.type.type != ResourceType::kLayout) {
return false;
}
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 47c804c..5372cf2 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -352,7 +352,7 @@
}
const ResourceName& ref_name = ref.name.value();
- CHECK_EQ(ref_name.type, ResourceType::kAttr);
+ CHECK_EQ(ref_name.type.type, ResourceType::kAttr);
if (!ref_name.package.empty()) {
*out_msg << ref_name.package << ":";
@@ -385,7 +385,7 @@
Reference transformed_reference = reference;
xml::ResolvePackage(decls, &transformed_reference);
- if (transformed_reference.name.value().type == ResourceType::kMacro) {
+ if (transformed_reference.name.value().type.type == ResourceType::kMacro) {
if (transformed_reference.name.value().package.empty()) {
transformed_reference.name.value().package = callsite.package;
}
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index d094d36..d78f0ac 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -349,7 +349,7 @@
file_ref->file = file;
ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
- pkg->FindOrCreateType(file_desc.name.type)
+ pkg->FindOrCreateType(file_desc.name.type.type)
->FindOrCreateEntry(file_desc.name.entry)
->FindOrCreateValue(file_desc.config, {})
->value = std::move(file_ref);
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 2d58cbf..92b45c3 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -185,7 +185,7 @@
const ResourceName& name) {
std::optional<ResourceTable::SearchResult> result = table_->FindResource(name);
if (!result) {
- if (name.type == ResourceType::kAttr) {
+ if (name.type.type == ResourceType::kAttr) {
// Recurse and try looking up a private attribute.
return FindByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
}
@@ -203,7 +203,7 @@
(sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
}
- if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
+ if (name.type.type == ResourceType::kAttr || name.type.type == ResourceType::kAttrPrivate) {
const ConfigDescription kDefaultConfig;
ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig);
if (config_value) {
@@ -366,7 +366,7 @@
}
std::unique_ptr<SymbolTable::Symbol> s;
- if (real_name.type == ResourceType::kAttr) {
+ if (real_name.type.type == ResourceType::kAttr) {
s = LookupAttributeInTable(asset_manager_, res_id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
@@ -413,7 +413,7 @@
ResourceName& name = maybe_name.value();
std::unique_ptr<SymbolTable::Symbol> s;
- if (name.type == ResourceType::kAttr) {
+ if (name.type.type == ResourceType::kAttr) {
s = LookupAttributeInTable(asset_manager_, id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 65ae7be..c17837c 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -38,7 +38,7 @@
std::hash<std::string> str_hash;
android::hash_t hash = 0;
hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.package));
- hash = android::JenkinsHashMix(hash, (uint32_t)name.type);
+ hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.type.name));
hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.entry));
return hash;
}