Merge "AudioService: do not update ringer mode with broker lock held"
diff --git a/.gitignore b/.gitignore
index 45884c4..d7aebc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/.idea
*.iml
+*.sw*
diff --git a/Android.bp b/Android.bp
index 995fe3e..072007b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -209,6 +209,7 @@
"core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
"core/java/android/hardware/usb/IUsbManager.aidl",
"core/java/android/hardware/usb/IUsbSerialReader.aidl",
+ "core/java/android/net/ICaptivePortal.aidl",
"core/java/android/net/IConnectivityManager.aidl",
"core/java/android/hardware/ISensorPrivacyListener.aidl",
"core/java/android/hardware/ISensorPrivacyManager.aidl",
@@ -893,7 +894,6 @@
srcs: [
"core/java/android/net/ApfCapabilitiesParcelable.aidl",
"core/java/android/net/DhcpResultsParcelable.aidl",
- "core/java/android/net/ICaptivePortal.aidl",
"core/java/android/net/INetworkMonitor.aidl",
"core/java/android/net/INetworkMonitorCallbacks.aidl",
"core/java/android/net/IIpMemoryStore.aidl",
diff --git a/api/current.txt b/api/current.txt
index d11b6a4..15c4fdf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14096,6 +14096,34 @@
ctor @Deprecated public EmbossMaskFilter(float[], float, float, float);
}
+ public class HardwareRenderer {
+ ctor public HardwareRenderer();
+ method public void clearContent();
+ method public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest();
+ method public void destroy();
+ method public boolean isOpaque();
+ method public void notifyFramePending();
+ method public void setContentRoot(@Nullable android.graphics.RenderNode);
+ method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+ method public void setLightSourceGeometry(float, float, float, float);
+ method public void setName(String);
+ method public void setOpaque(boolean);
+ method public void setStopped(boolean);
+ method public void setSurface(@Nullable android.view.Surface);
+ field public static final int SYNC_CONTEXT_IS_STOPPED = 4; // 0x4
+ field public static final int SYNC_FRAME_DROPPED = 8; // 0x8
+ field public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 2; // 0x2
+ field public static final int SYNC_OK = 0; // 0x0
+ field public static final int SYNC_REDRAW_REQUESTED = 1; // 0x1
+ }
+
+ public final class HardwareRenderer.FrameRenderRequest {
+ method public android.graphics.HardwareRenderer.FrameRenderRequest setFrameCommitCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable);
+ method public android.graphics.HardwareRenderer.FrameRenderRequest setVsyncTime(long);
+ method public android.graphics.HardwareRenderer.FrameRenderRequest setWaitForPresent(boolean);
+ method public int syncAndDraw();
+ }
+
public final class ImageDecoder implements java.lang.AutoCloseable {
method public void close();
method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(@NonNull android.content.res.Resources, int);
@@ -35052,7 +35080,7 @@
method public android.os.PowerManager.WakeLock newWakeLock(int, String);
method public void reboot(String);
method public void registerThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback, @NonNull java.util.concurrent.Executor);
- method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback);
+ method public void unregisterThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback);
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
@@ -41955,7 +41983,6 @@
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
- method public final void clearTranscription(boolean);
method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
@@ -41965,8 +41992,7 @@
method public void onReady();
method public void onShutdown();
method public void setDisabledShowContext(int);
- method public final void setTranscription(@NonNull String);
- method public final void setVoiceState(int);
+ method public final void setUiHints(@NonNull android.os.Bundle);
method public void showSession(android.os.Bundle, int);
field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
field public static final String SERVICE_META_DATA = "android.voice_interaction";
@@ -55745,6 +55771,7 @@
method public int getDropDownVerticalOffset();
method public int getDropDownWidth();
method protected android.widget.Filter getFilter();
+ method public int getInputMethodMode();
method @Deprecated public android.widget.AdapterView.OnItemClickListener getItemClickListener();
method @Deprecated public android.widget.AdapterView.OnItemSelectedListener getItemSelectedListener();
method public int getListSelection();
@@ -55769,6 +55796,7 @@
method public void setDropDownHorizontalOffset(int);
method public void setDropDownVerticalOffset(int);
method public void setDropDownWidth(int);
+ method public void setInputMethodMode(int);
method public void setListSelection(int);
method public void setOnDismissListener(android.widget.AutoCompleteTextView.OnDismissListener);
method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index 4eb285b..fb2b9e1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -285,9 +285,11 @@
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
+ method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+ method public void setDeviceLocales(@NonNull android.os.LocaleList);
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
}
@@ -312,6 +314,7 @@
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
+ field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
@@ -545,15 +548,17 @@
}
public class StatusBarManager {
- method public android.util.Pair<java.lang.Integer,java.lang.Integer> getDisableFlags();
+ method public android.app.StatusBarManager.DisableInfo getDisableInfo();
method public void setDisabledForSetup(boolean);
- field public static final int DISABLE2_NONE = 0; // 0x0
- field public static final int DISABLE_EXPAND = 65536; // 0x10000
- field public static final int DISABLE_HOME = 2097152; // 0x200000
- field public static final int DISABLE_NONE = 0; // 0x0
- field public static final int DISABLE_NOTIFICATION_ALERTS = 262144; // 0x40000
- field public static final int DISABLE_RECENT = 16777216; // 0x1000000
- field public static final int DISABLE_SEARCH = 33554432; // 0x2000000
+ }
+
+ public static final class StatusBarManager.DisableInfo {
+ method public boolean areNoComponentsDisabled();
+ method public boolean isNavigateToHomeDisabled();
+ method public boolean isNotificationPeekingDisabled();
+ method public boolean isRecentsDisabled();
+ method public boolean isSearchDisabled();
+ method public boolean isStatusBarExpansionDisabled();
}
public final class Vr2dDisplayProperties implements android.os.Parcelable {
@@ -1366,6 +1371,7 @@
field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+ field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
field public static final String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
@@ -1376,6 +1382,7 @@
field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
+ field public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
field public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
@@ -3926,7 +3933,7 @@
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
- method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
@@ -5302,6 +5309,10 @@
field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
}
+ public final class LocaleList implements android.os.Parcelable {
+ method public static boolean isPseudoLocale(@Nullable android.icu.util.ULocale);
+ }
+
public final class NativeHandle implements java.io.Closeable {
ctor public NativeHandle();
ctor public NativeHandle(@NonNull java.io.FileDescriptor, boolean);
@@ -6339,7 +6350,8 @@
method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
method public void onDisconnected();
method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
- method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
}
@@ -9296,7 +9308,8 @@
package android.view.autofill {
public final class AutofillManager {
- method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
}
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 8194785..2cb01ea 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -936,7 +936,7 @@
}
public class ConnectivityManager {
- method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
}
@@ -996,6 +996,7 @@
method public int[] getCapabilities();
method public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
+ field public static final int TRANSPORT_TEST = 7; // 0x7
}
public class NetworkStack {
@@ -2102,7 +2103,8 @@
method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
method public void onDisconnected();
method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
- method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
}
@@ -2691,7 +2693,8 @@
}
public final class AutofillManager {
- method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+ method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes";
field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0
field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 46917e4..a6c7cae 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -110,13 +110,30 @@
} else {
mShuttingDown = true;
}
+ ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+ elapsedRealtime());
+}
+
+BootAnimation::~BootAnimation() {
+ if (mAnimation != nullptr) {
+ releaseAnimation(mAnimation);
+ mAnimation = nullptr;
+ }
+ ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+ elapsedRealtime());
}
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
- run("BootAnimation", PRIORITY_DISPLAY);
+ // Load the animation content -- this can be slow (eg 200ms)
+ // called before waitForSurfaceFlinger() in main() to avoid wait
+ ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
+ mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
+ preloadAnimation();
+ ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
+ mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
}
}
@@ -306,6 +323,20 @@
mFlingerSurface = s;
mTargetInset = -1;
+ return NO_ERROR;
+}
+
+bool BootAnimation::preloadAnimation() {
+ findBootAnimationFile();
+ if (!mZipFileName.isEmpty()) {
+ mAnimation = loadAnimation(mZipFileName);
+ return (mAnimation != nullptr);
+ }
+
+ return false;
+}
+
+void BootAnimation::findBootAnimationFile() {
// If the device has encryption turned on or is in process
// of being encrypted we show the encrypted boot animation.
char decrypt[PROPERTY_VALUE_MAX];
@@ -320,7 +351,7 @@
for (const char* f : encryptedBootFiles) {
if (access(f, R_OK) == 0) {
mZipFileName = f;
- return NO_ERROR;
+ return;
}
}
}
@@ -332,10 +363,9 @@
for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
if (access(f, R_OK) == 0) {
mZipFileName = f;
- return NO_ERROR;
+ return;
}
}
- return NO_ERROR;
}
bool BootAnimation::threadLoop()
@@ -790,8 +820,6 @@
}
}
- mCallbacks->init(animation.parts);
-
zip->endIteration(cookie);
return true;
@@ -799,13 +827,25 @@
bool BootAnimation::movie()
{
- Animation* animation = loadAnimation(mZipFileName);
- if (animation == NULL)
+ if (mAnimation == nullptr) {
+ mAnimation = loadAnimation(mZipFileName);
+ }
+
+ if (mAnimation == nullptr)
return false;
+ // mCallbacks->init() may get called recursively,
+ // this loop is needed to get the same results
+ for (const Animation::Part& part : mAnimation->parts) {
+ if (part.animation != nullptr) {
+ mCallbacks->init(part.animation->parts);
+ }
+ }
+ mCallbacks->init(mAnimation->parts);
+
bool anyPartHasClock = false;
- for (size_t i=0; i < animation->parts.size(); i++) {
- if(validClock(animation->parts[i])) {
+ for (size_t i=0; i < mAnimation->parts.size(); i++) {
+ if(validClock(mAnimation->parts[i])) {
anyPartHasClock = true;
break;
}
@@ -846,7 +886,7 @@
bool clockFontInitialized = false;
if (mClockEnabled) {
clockFontInitialized =
- (initFont(&animation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
+ (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
mClockEnabled = clockFontInitialized;
}
@@ -855,7 +895,7 @@
mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
}
- playAnimation(*animation);
+ playAnimation(*mAnimation);
if (mTimeCheckThread != nullptr) {
mTimeCheckThread->requestExit();
@@ -863,10 +903,11 @@
}
if (clockFontInitialized) {
- glDeleteTextures(1, &animation->clockFont.texture.name);
+ glDeleteTextures(1, &mAnimation->clockFont.texture.name);
}
- releaseAnimation(animation);
+ releaseAnimation(mAnimation);
+ mAnimation = nullptr;
return false;
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 19616cb..dc19fb0 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -115,6 +115,7 @@
};
explicit BootAnimation(sp<Callbacks> callbacks);
+ virtual ~BootAnimation();
sp<SurfaceComposerClient> session() const;
@@ -155,6 +156,8 @@
void releaseAnimation(Animation*) const;
bool parseAnimationDesc(Animation&);
bool preloadZip(Animation &animation);
+ void findBootAnimationFile();
+ bool preloadAnimation();
void checkExit();
@@ -182,6 +185,7 @@
SortedVector<String8> mLoadedFiles;
sp<TimeCheckThread> mTimeCheckThread = nullptr;
sp<Callbacks> mCallbacks;
+ Animation* mAnimation = nullptr;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index a52a5e9..6c7b3e5 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -44,14 +44,16 @@
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
+ // create the boot animation object (may take up to 200ms for 2MB zip)
+ sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+
waitForSurfaceFlinger();
- // create the boot animation object
- sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+ boot->run("BootAnimation", PRIORITY_DISPLAY);
+
ALOGV("Boot animation set up. Joining pool.");
IPCThreadState::self()->joinThreadPool();
}
- ALOGV("Boot animation exit");
return 0;
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 873b772..57c32c7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -302,6 +302,7 @@
RoleHolder role_holder = 10049;
DangerousPermissionState dangerous_permission_state = 10050;
TrainInfo train_info = 10051;
+ TimeZoneDataInfo time_zone_data_info = 10052;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -5564,3 +5565,11 @@
// [0,100] progress for the assist gesture.
optional int32 progress = 1;
}
+
+/*
+ * Information about the time zone data on a device.
+ */
+message TimeZoneDataInfo {
+ // A version identifier for the data set on device. e.g. "2018i"
+ optional string tzdb_version = 1;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 98f810f..d70c851 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -234,6 +234,9 @@
{.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
// TrainInfo.
{android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}},
+ // TimeZoneDataInfo.
+ {android::util::TIME_ZONE_DATA_INFO,
+ {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 5f3aae3..4579ca6 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -297,7 +297,8 @@
// Setup a simple config, no activation
StatsdConfig config1;
- config1.set_id(12341);
+ int64_t cfgId1 = 12341;
+ config1.set_id(cfgId1);
config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config1.add_atom_matcher() = wakelockAcquireMatcher;
@@ -314,14 +315,12 @@
countMetric2->set_what(wakelockAcquireMatcher.id());
countMetric2->set_bucket(FIVE_MINUTES);
- ConfigKey cfgKey1(uid, 12341);
- long timeBase1 = 1;
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+ ConfigKey cfgKey1(uid, cfgId1);
// Add another config, with two metrics, one with activation
StatsdConfig config2;
- config2.set_id(12342);
+ int64_t cfgId2 = 12342;
+ config2.set_id(cfgId2);
config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config2.add_atom_matcher() = wakelockAcquireMatcher;
@@ -344,11 +343,12 @@
metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
metric3ActivationTrigger->set_ttl_seconds(100);
- ConfigKey cfgKey2(uid, 12342);
+ ConfigKey cfgKey2(uid, cfgId2);
// Add another config, with two metrics, both with activations
StatsdConfig config3;
- config3.set_id(12342);
+ int64_t cfgId3 = 12343;
+ config3.set_id(cfgId3);
config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config3.add_atom_matcher() = wakelockAcquireMatcher;
@@ -376,14 +376,37 @@
metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
metric6ActivationTrigger->set_ttl_seconds(200);
- ConfigKey cfgKey3(uid, 12343);
+ ConfigKey cfgKey3(uid, cfgId3);
- processor->OnConfigUpdated(2, cfgKey2, config2);
- processor->OnConfigUpdated(3, cfgKey3, config3);
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ vector<int64_t> activeConfigsBroadcast;
- EXPECT_EQ(3, processor->mMetricsManagers.size());
- auto it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ long timeBase1 = 1;
+ int broadcastCount = 0;
+ StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+ timeBase1, [](const ConfigKey& key) { return true; },
+ [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+ const vector<int64_t>& activeConfigs) {
+ broadcastCount++;
+ EXPECT_EQ(broadcastUid, uid);
+ activeConfigsBroadcast.clear();
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+ activeConfigs.begin(), activeConfigs.end());
+ return true;
+ });
+
+ processor.OnConfigUpdated(1, cfgKey1, config1);
+ processor.OnConfigUpdated(2, cfgKey2, config2);
+ processor.OnConfigUpdated(3, cfgKey3, config3);
+
+ EXPECT_EQ(3, processor.mMetricsManagers.size());
+
+ // Expect the first config and both metrics in it to be active.
+ auto it = processor.mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor.mMetricsManagers.end());
auto& metricsManager1 = it->second;
EXPECT_TRUE(metricsManager1->isActive());
@@ -407,8 +430,9 @@
auto& metricProducer2 = *metricIt;
EXPECT_TRUE(metricProducer2->isActive());
- it = processor->mMetricsManagers.find(cfgKey2);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
+ it = processor.mMetricsManagers.find(cfgKey2);
+ EXPECT_TRUE(it != processor.mMetricsManagers.end());
auto& metricsManager2 = it->second;
EXPECT_TRUE(metricsManager2->isActive());
@@ -432,8 +456,9 @@
auto& metricProducer4 = *metricIt;
EXPECT_TRUE(metricProducer4->isActive());
- it = processor->mMetricsManagers.find(cfgKey3);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ // Expect the third config and both metrics in it to be inactive.
+ it = processor.mMetricsManagers.find(cfgKey3);
+ EXPECT_TRUE(it != processor.mMetricsManagers.end());
auto& metricsManager3 = it->second;
EXPECT_FALSE(metricsManager3->isActive());
@@ -457,10 +482,30 @@
auto& metricProducer6 = *metricIt;
EXPECT_FALSE(metricProducer6->isActive());
+ // No broadcast for active configs should have happened yet.
+ EXPECT_EQ(broadcastCount, 0);
+
+ // Activate all 3 metrics that were not active.
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
+ // Assert that all 3 configs are active.
+ EXPECT_TRUE(metricsManager1->isActive());
+ EXPECT_TRUE(metricsManager2->isActive());
+ EXPECT_TRUE(metricsManager3->isActive());
+
+ // A broadcast should have happened, and all 3 configs should be active in the broadcast.
+ EXPECT_EQ(broadcastCount, 1);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 3);
+ EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1)
+ != activeConfigsBroadcast.end());
+ EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2)
+ != activeConfigsBroadcast.end());
+ EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3)
+ != activeConfigsBroadcast.end());
+
+ // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
EXPECT_TRUE(metricProducer3->isActive());
int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime);
@@ -472,8 +517,9 @@
int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime);
EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6);
- processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC);
+ processor.WriteMetricsActivationToDisk(shutDownTime);
+ // Create a second StatsLogProcessor and push the same 3 configs.
long timeBase2 = 1000;
sp<StatsLogProcessor> processor2 =
CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
@@ -481,6 +527,8 @@
processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
EXPECT_EQ(3, processor2->mMetricsManagers.size());
+
+ // First config and both metrics are active.
it = processor2->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
auto& metricsManager1001 = it->second;
@@ -506,6 +554,7 @@
auto& metricProducer1002 = *metricIt;
EXPECT_TRUE(metricProducer1002->isActive());
+ // Second config is active. Metric 3 is inactive, metric 4 is active.
it = processor2->mMetricsManagers.find(cfgKey2);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
auto& metricsManager1002 = it->second;
@@ -531,6 +580,7 @@
auto& metricProducer1004 = *metricIt;
EXPECT_TRUE(metricProducer1004->isActive());
+ // Config 3 is inactive. both metrics are inactive.
it = processor2->mMetricsManagers.find(cfgKey3);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
auto& metricsManager1003 = it->second;
@@ -557,6 +607,7 @@
auto& metricProducer1006 = *metricIt;
EXPECT_FALSE(metricProducer1006->isActive());
+ // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
EXPECT_FALSE(metricProducer1003->isActive());
const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns);
@@ -572,12 +623,16 @@
processor2->LoadMetricsActivationFromDisk();
+ // After loading activations from disk, assert that all 3 metrics are active.
EXPECT_TRUE(metricProducer1003->isActive());
EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns);
EXPECT_TRUE(metricProducer1005->isActive());
EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns);
EXPECT_TRUE(metricProducer1006->isActive());
EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns);
+
+ // Make sure no more broadcasts have happened.
+ EXPECT_EQ(broadcastCount, 1);
}
TEST(StatsLogProcessorTest, TestActivationOnBoot) {
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index 29e86f3..85d8a56 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -66,17 +66,42 @@
auto config = CreateStatsdConfig();
int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- sp<MetricProducer> metricProducer =
- processor->mMetricsManagers.begin()->second->mAllMetricProducers[0];
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ vector<int64_t> activeConfigsBroadcast;
+
+ long timeBase1 = 1;
+ int broadcastCount = 0;
+ StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+ bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+ const vector<int64_t>& activeConfigs) {
+ broadcastCount++;
+ EXPECT_EQ(broadcastUid, uid);
+ activeConfigsBroadcast.clear();
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+ activeConfigs.begin(), activeConfigs.end());
+ return true;
+ });
+
+ processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
+
+ EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
+ EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// Two activations: one is triggered by battery saver mode (tracker index 0), the other is
// triggered by screen on event (tracker index 2).
@@ -93,13 +118,19 @@
std::unique_ptr<LogEvent> event;
event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 0);
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 1);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -109,12 +140,13 @@
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
// Activated by screen on event.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 20);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
@@ -126,7 +158,8 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
@@ -134,15 +167,21 @@
EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+ // No new broadcast since the config should still be active.
+ EXPECT_EQ(broadcastCount, 1);
// 3rd processed event.
event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
// All activations expired.
event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
+ // New broadcast since the config is no longer active.
+ EXPECT_EQ(broadcastCount, 2);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -153,8 +192,12 @@
// Re-activate.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 3);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -163,11 +206,11 @@
EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor->OnLogEvent(event.get());
+ processor.OnLogEvent(event.get());
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+ processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ca3c726..5d4f988 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -55,6 +55,7 @@
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -68,6 +69,7 @@
import android.util.Singleton;
import android.util.Size;
+import com.android.internal.app.LocalePicker;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.os.TransferPipe;
@@ -84,7 +86,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Locale;
/**
* <p>
@@ -3451,6 +3455,35 @@
}
/**
+ * Sets the current locales of the device. Calling app must have the permission
+ * {@code android.permission.CHANGE_CONFIGURATION} and
+ * {@code android.permission.WRITE_SETTINGS}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setDeviceLocales(@NonNull LocaleList locales) {
+ LocalePicker.updateLocales(locales);
+ }
+
+ /**
+ * Returns a list of supported locales by this system. It includes all locales that are
+ * selectable by the user, potentially including locales that the framework does not have
+ * translated resources for. To get locales that the framework has translated resources for, use
+ * {@code Resources.getSystem().getAssets().getLocales()} instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Collection<Locale> getSupportedLocales() {
+ ArrayList<Locale> locales = new ArrayList<>();
+ for (String localeTag : LocalePicker.getSupportedLocales(mContext)) {
+ locales.add(Locale.forLanguageTag(localeTag));
+ }
+ return locales;
+ }
+
+ /**
* Get the device configuration attributes.
*/
public ConfigurationInfo getDeviceConfigurationInfo() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 64b94a9..f76f7b9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -848,6 +848,7 @@
/** @hide Has a legacy (non-isolated) view of storage. */
public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
/** @hide Interact with accessibility. */
+ @SystemApi
public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
// Warning: If an permission is added here it also has to be added to
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 1878d84..077652c 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -42,12 +42,10 @@
public class StatusBarManager {
/** @hide */
- @SystemApi
public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
/** @hide */
public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
/** @hide */
- @SystemApi
public static final int DISABLE_NOTIFICATION_ALERTS
= View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
@@ -59,17 +57,14 @@
/** @hide */
public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
/** @hide */
- @SystemApi
public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
/** @hide */
- @SystemApi
public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
/** @hide */
public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
/** @hide */
public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
/** @hide */
- @SystemApi
public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;
/** @hide */
@@ -78,7 +73,6 @@
View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;
/** @hide */
- @SystemApi
public static final int DISABLE_NONE = 0x00000000;
/** @hide */
@@ -122,7 +116,6 @@
public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4;
/** @hide */
- @SystemApi
public static final int DISABLE2_NONE = 0x00000000;
/** @hide */
@@ -387,14 +380,14 @@
}
/**
- * Get the currently applied StatusBar disable flags
+ * Get this app's currently requested disabled components
*
- * @return a pair of Integers in the form of (disable, disable2)
+ * @return a new DisableInfo
*
* @hide
*/
@SystemApi
- public Pair<Integer, Integer> getDisableFlags() {
+ public DisableInfo getDisableInfo() {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
final IStatusBarService svc = getService();
@@ -403,7 +396,7 @@
flags = svc.getDisableFlags(mToken, userId);
}
- return new Pair<Integer, Integer>(flags[0], flags[1]);
+ return new DisableInfo(flags[0], flags[1]);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -416,4 +409,180 @@
if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING";
return "WINDOW_STATE_UNKNOWN";
}
+
+ /**
+ * DisableInfo describes this app's requested state of the StatusBar with regards to which
+ * components are enabled/disabled
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class DisableInfo {
+
+ private boolean mStatusBarExpansion;
+ private boolean mNavigateHome;
+ private boolean mNotificationPeeking;
+ private boolean mRecents;
+ private boolean mSearch;
+
+ /** @hide */
+ public DisableInfo(int flags1, int flags2) {
+ mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0;
+ mNavigateHome = (flags1 & DISABLE_HOME) != 0;
+ mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0;
+ mRecents = (flags1 & DISABLE_RECENT) != 0;
+ mSearch = (flags1 & DISABLE_SEARCH) != 0;
+ }
+
+ /** @hide */
+ public DisableInfo() {}
+
+ /**
+ * @return {@code true} if expanding the notification shade is disabled
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isStatusBarExpansionDisabled() {
+ return mStatusBarExpansion;
+ }
+
+ /** * @hide */
+ public void setStatusBarExpansionDisabled(boolean disabled) {
+ mStatusBarExpansion = disabled;
+ }
+
+ /**
+ * @return {@code true} if navigation home is disabled
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isNavigateToHomeDisabled() {
+ return mNavigateHome;
+ }
+
+ /** * @hide */
+ public void setNagivationHomeDisabled(boolean disabled) {
+ mNavigateHome = disabled;
+ }
+
+ /**
+ * @return {@code true} if notification peeking (heads-up notification) is disabled
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isNotificationPeekingDisabled() {
+ return mNotificationPeeking;
+ }
+
+ /** @hide */
+ public void setNotificationPeekingDisabled(boolean disabled) {
+ mNotificationPeeking = disabled;
+ }
+
+ /**
+ * @return {@code true} if mRecents/overview is disabled
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isRecentsDisabled() {
+ return mRecents;
+ }
+
+ /** @hide */
+ public void setRecentsDisabled(boolean disabled) {
+ mRecents = disabled;
+ }
+
+ /**
+ * @return {@code true} if mSearch is disabled
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isSearchDisabled() {
+ return mSearch;
+ }
+
+ /** @hide */
+ public void setSearchDisabled(boolean disabled) {
+ mSearch = disabled;
+ }
+
+ /**
+ * @return {@code true} if no components are disabled (default state)
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean areNoComponentsDisabled() {
+ return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
+ && !mSearch;
+ }
+
+ /** @hide */
+ public void setEnableAll() {
+ mStatusBarExpansion = false;
+ mNavigateHome = false;
+ mNotificationPeeking = false;
+ mRecents = false;
+ mSearch = false;
+ }
+
+ /**
+ * @return {@code true} if all status bar components are disabled
+ *
+ * @hide
+ */
+ public boolean areAllComponentsDisabled() {
+ return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
+ && mRecents && mSearch;
+ }
+
+ /** @hide */
+ public void setDisableAll() {
+ mStatusBarExpansion = true;
+ mNavigateHome = true;
+ mNotificationPeeking = true;
+ mRecents = true;
+ mSearch = true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("DisableInfo: ");
+ sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled");
+ sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled");
+ sb.append(" mNotificationPeeking=")
+ .append(mNotificationPeeking ? "disabled" : "enabled");
+ sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled");
+ sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled");
+
+ return sb.toString();
+
+ }
+
+ /**
+ * Convert a DisableInfo to equivalent flags
+ * @return a pair of equivalent disable flags
+ *
+ * @hide
+ */
+ public Pair<Integer, Integer> toFlags() {
+ int disable1 = DISABLE_NONE;
+ int disable2 = DISABLE2_NONE;
+
+ if (mStatusBarExpansion) disable1 |= DISABLE_EXPAND;
+ if (mNavigateHome) disable1 |= DISABLE_HOME;
+ if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS;
+ if (mRecents) disable1 |= DISABLE_RECENT;
+ if (mSearch) disable1 |= DISABLE_SEARCH;
+
+ return new Pair<Integer, Integer>(disable1, disable2);
+ }
+ }
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index cca8bd0..b9f56b1 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -106,7 +106,6 @@
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
-import android.net.NetworkStack;
import android.net.NetworkWatchlistManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
@@ -336,13 +335,6 @@
}
});
- registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
- new StaticServiceFetcher<NetworkStack>() {
- @Override
- public NetworkStack createService() {
- return new NetworkStack();
- }});
-
registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class,
new CachedServiceFetcher<IpMemoryStore>() {
@Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 957a484..25bfba2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -49,7 +49,6 @@
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import android.net.NetworkStack;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -3647,11 +3646,10 @@
public static final String NETD_SERVICE = "netd";
/**
- * Use with {@link #getSystemService(String)} to retrieve a
- * {@link NetworkStack} for communicating with the network stack
+ * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+ * {@link NetworkStackClient} IBinder for communicating with the network stack
* @hide
- * @see #getSystemService(String)
- * @see NetworkStack
+ * @see NetworkStackClient
*/
public static final String NETWORK_STACK_SERVICE = "network_stack";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d781a96..a5e7e95 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1790,6 +1790,35 @@
"android.intent.action.MANAGE_APP_PERMISSIONS";
/**
+ * Activity action: Launch UI to manage a specific permissions of an app.
+ * <p>
+ * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
+ * will be managed by the launched UI.
+ * </p>
+ * <p>
+ * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
+ * that should be managed by the launched UI.
+ * </p>
+ * <p>
+ * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @see #EXTRA_PACKAGE_NAME
+ * @see #EXTRA_PERMISSION_NAME
+ * @see #EXTRA_USER
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_APP_PERMISSION =
+ "android.intent.action.MANAGE_APP_PERMISSION";
+
+ /**
* Activity action: Launch UI to manage permissions.
* <p>
* Input: Nothing.
@@ -2080,6 +2109,22 @@
public static final String ACTION_REVIEW_APP_PERMISSION_USAGE =
"android.intent.action.REVIEW_APP_PERMISSION_USAGE";
+ /**
+ * Activity action: Launch UI to review running accessibility services.
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES =
+ "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent broadcast actions (see action variable).
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index cfe35b0..270e387 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -163,6 +163,30 @@
}
/**
+ * Provider for default home
+ */
+ public interface DefaultHomeProvider {
+
+ /**
+ * Get the package name of the default home.
+ *
+ * @param userId the user id
+ *
+ * @return the package name of the default home, or {@code null} if none
+ */
+ @Nullable
+ String getDefaultHome(@UserIdInt int userId);
+
+ /**
+ * Set the package name of the default home.
+ *
+ * @param packageName package name of the default home, or {@code null} to remove
+ * @param userId the user id
+ */
+ void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId);
+ }
+
+ /**
* Sets the location provider packages provider.
* @param provider The packages provider.
*/
@@ -886,4 +910,11 @@
* @param provider the provider
*/
public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider);
+
+ /**
+ * Sets the default home provider.
+ *
+ * @param provider the provider
+ */
+ public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider);
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d257c03..a696eeb 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -156,21 +156,21 @@
}
/**
- * Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
*
* @param token an opaque token returned by password confirmation.
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void resetTimeout(byte[] token) {
+ public void resetLockout(byte[] token) {
if (mService != null) {
try {
- mService.resetTimeout(token);
+ mService.resetLockout(token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
- Slog.w(TAG, "resetTimeout(): Service not connected");
+ Slog.w(TAG, "resetLockout(): Service not connected");
}
}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index a20e2bf..4971911 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -49,8 +49,8 @@
// Client lifecycle is still managed in <Biometric>Service.
void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId);
- // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
- void resetTimeout(in byte [] token);
+ // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ void resetLockout(in byte [] token);
// TODO(b/123378871): Remove when moved.
// CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index efe24e5..55b340f 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -107,6 +107,11 @@
mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
new Face(null, faceId, deviceId)).sendToTarget();
}
+
+ @Override
+ public void onEnumerated(long deviceId, int faceId, int remaining) {
+ // TODO: Finish. Low priority since it's not used.
+ }
};
/**
@@ -474,25 +479,6 @@
}
/**
- * Reset the lockout timer when asked to do so by keyguard.
- *
- * @param token an opaque token returned by password confirmation.
- * @hide
- */
- @RequiresPermission(MANAGE_BIOMETRIC)
- public void resetTimeout(byte[] token) {
- if (mService != null) {
- try {
- mService.resetTimeout(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Log.w(TAG, "resetTimeout(): Service not connected!");
- }
- }
-
- /**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index f67760a..9609e99 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -86,8 +86,8 @@
// Gets the authenticator ID for face
long getAuthenticatorId(String opPackageName);
- // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
- void resetTimeout(in byte [] cryptoToken);
+ // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ void resetLockout(in byte [] token);
// Add a callback which gets notified when the face lockout period expired.
void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index b88574b..cec9fd8 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -28,4 +28,5 @@
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int faceId, int remaining);
+ void onEnumerated(long deviceId, int faceId, int remaining);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 80d404d..d0622c8 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -18,7 +18,6 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_FINGERPRINT;
@@ -724,26 +723,6 @@
}
/**
- * Reset the lockout timer when asked to do so by keyguard.
- *
- * @param token an opaque token returned by password confirmation.
- *
- * @hide
- */
- @RequiresPermission(RESET_FINGERPRINT_LOCKOUT)
- public void resetTimeout(byte[] token) {
- if (mService != null) {
- try {
- mService.resetTimeout(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Slog.w(TAG, "resetTimeout(): Service not connected!");
- }
- }
-
- /**
* @hide
*/
public void addLockoutResetCallback(final LockoutResetCallback callback) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2aca55a..6d195ae 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -678,11 +678,20 @@
@Deprecated
public static final int TYPE_VPN = 17;
- /** {@hide} */
- public static final int MAX_RADIO_TYPE = TYPE_VPN;
+ /**
+ * A network that is exclusively meant to be used for testing
+ *
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @Deprecated
+ public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
/** {@hide} */
- public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+ public static final int MAX_RADIO_TYPE = TYPE_TEST;
+
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_TEST;
private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
@@ -3927,15 +3936,16 @@
*
* <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
* corresponding permission.
+ * @param network Network on which the captive portal was detected.
* @param appExtras Extras to include in the app start intent.
* @hide
*/
@SystemApi
@TestApi
@RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
- public void startCaptivePortalApp(Bundle appExtras) {
+ public void startCaptivePortalApp(Network network, Bundle appExtras) {
try {
- mService.startCaptivePortalAppInternal(appExtras);
+ mService.startCaptivePortalAppInternal(network, appExtras);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 872671f..87c62d2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -168,7 +168,7 @@
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
void setAvoidUnvalidated(in Network network);
void startCaptivePortalApp(in Network network);
- void startCaptivePortalAppInternal(in Bundle appExtras);
+ void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
boolean getAvoidBadWifi();
int getMultipathPreference(in Network Network);
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
index 41f969a..c94cdde 100644
--- a/core/java/android/net/INetworkMonitor.aidl
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -34,6 +34,7 @@
void start();
void launchCaptivePortalApp();
+ void notifyCaptivePortalAppFinished(int response);
void forceReevaluation(int uid);
void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
void notifyDnsResponse(int returnCode);
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
index 5146585..2c61511 100644
--- a/core/java/android/net/INetworkMonitorCallbacks.aidl
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -26,5 +26,4 @@
void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
void showProvisioningNotification(String action, String packageName);
void hideProvisioningNotification();
- void logCaptivePortalLoginEvent(int eventId, String packageName);
}
\ No newline at end of file
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7e9bda1..1d2d81d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -597,6 +597,7 @@
TRANSPORT_VPN,
TRANSPORT_WIFI_AWARE,
TRANSPORT_LOWPAN,
+ TRANSPORT_TEST,
})
public @interface Transport { }
@@ -635,10 +636,18 @@
*/
public static final int TRANSPORT_LOWPAN = 6;
+ /**
+ * Indicates this network uses a Test-only virtual interface as a transport.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int TRANSPORT_TEST = 7;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+ public static final int MAX_TRANSPORT = TRANSPORT_TEST;
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
@@ -652,7 +661,8 @@
"ETHERNET",
"VPN",
"WIFI_AWARE",
- "LOWPAN"
+ "LOWPAN",
+ "TEST"
};
/**
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index ca49438..dbb894f 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,46 +15,17 @@
*/
package android.net;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IIpClientCallbacks;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
/**
- * Service used to communicate with the network stack, which is running in a separate module.
+ *
+ * Constants for client code communicating with the network stack service.
* @hide
*/
-@SystemService(Context.NETWORK_STACK_SERVICE)
@SystemApi
@TestApi
public class NetworkStack {
- private static final String TAG = NetworkStack.class.getSimpleName();
-
/**
* Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature
* protection level.
@@ -65,235 +36,5 @@
public static final String PERMISSION_MAINLINE_NETWORK_STACK =
"android.permission.MAINLINE_NETWORK_STACK";
- private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
-
- @NonNull
- @GuardedBy("mPendingNetStackRequests")
- private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
- @Nullable
- @GuardedBy("mPendingNetStackRequests")
- private INetworkStackConnector mConnector;
-
- private volatile boolean mNetworkStackStartRequested = false;
-
- private interface NetworkStackCallback {
- void onNetworkStackConnected(INetworkStackConnector connector);
- }
-
- /** @hide */
- public NetworkStack() { }
-
- /**
- * Create a DHCP server according to the specified parameters.
- *
- * <p>The server will be returned asynchronously through the provided callbacks.
- * @hide
- */
- public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
- final IDhcpServerCallbacks cb) {
- requestConnector(connector -> {
- try {
- connector.makeDhcpServer(ifName, params, cb);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- /**
- * Create an IpClient on the specified interface.
- *
- * <p>The IpClient will be returned asynchronously through the provided callbacks.
- * @hide
- */
- public void makeIpClient(String ifName, IIpClientCallbacks cb) {
- requestConnector(connector -> {
- try {
- connector.makeIpClient(ifName, cb);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- /**
- * Create a NetworkMonitor.
- *
- * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
- * @hide
- */
- public void makeNetworkMonitor(
- NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
- requestConnector(connector -> {
- try {
- connector.makeNetworkMonitor(network, name, cb);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- private class NetworkStackConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- registerNetworkStackService(service);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // TODO: crash/reboot the system ?
- Slog.wtf(TAG, "Lost network stack connector");
- }
- };
-
- private void registerNetworkStackService(@NonNull IBinder service) {
- final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
-
- ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
- DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
-
- final ArrayList<NetworkStackCallback> requests;
- synchronized (mPendingNetStackRequests) {
- requests = new ArrayList<>(mPendingNetStackRequests);
- mPendingNetStackRequests.clear();
- mConnector = connector;
- }
-
- for (NetworkStackCallback r : requests) {
- r.onNetworkStackConnected(connector);
- }
- }
-
- /**
- * Start the network stack. Should be called only once on device startup.
- *
- * <p>This method will start the network stack either in the network stack process, or inside
- * the system server on devices that do not support the network stack module. The network stack
- * connector will then be delivered asynchronously to clients that requested it before it was
- * started.
- * @hide
- */
- public void start(Context context) {
- mNetworkStackStartRequested = true;
- // Try to bind in-process if the library is available
- IBinder connector = null;
- try {
- final Class service = Class.forName(
- "com.android.server.NetworkStackService",
- true /* initialize */,
- context.getClassLoader());
- connector = (IBinder) service.getMethod("makeConnector", Context.class)
- .invoke(null, context);
- } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
- // TODO: crash/reboot system here ?
- return;
- } catch (ClassNotFoundException e) {
- // Normal behavior if stack is provided by the app: fall through
- }
-
- // In-process network stack. Add the service to the service manager here.
- if (connector != null) {
- registerNetworkStackService(connector);
- return;
- }
- // Start the network stack process. The service will be added to the service manager in
- // NetworkStackConnection.onServiceConnected().
- final Intent intent = new Intent(INetworkStackConnector.class.getName());
- final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
- intent.setComponent(comp);
-
- if (comp == null) {
- Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
- // TODO: crash/reboot system server ?
- return;
- }
-
- final PackageManager pm = context.getPackageManager();
- int uid = -1;
- try {
- uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.wtf("Network stack package not found", e);
- // Fall through
- }
-
- if (uid != Process.NETWORK_STACK_UID) {
- throw new SecurityException("Invalid network stack UID: " + uid);
- }
-
- final int hasPermission =
- pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
- if (hasPermission != PERMISSION_GRANTED) {
- throw new SecurityException(
- "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
- }
-
- if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
- Slog.wtf(TAG,
- "Could not bind to network stack in-process, or in app with " + intent);
- // TODO: crash/reboot system server if no network stack after a timeout ?
- }
- }
-
- /**
- * For non-system server clients, get the connector registered by the system server.
- */
- private INetworkStackConnector getRemoteConnector() {
- // Block until the NetworkStack connector is registered in ServiceManager.
- // <p>This is only useful for non-system processes that do not have a way to be notified of
- // registration completion. Adding a callback system would be too heavy weight considering
- // that the connector is registered on boot, so it is unlikely that a client would request
- // it before it is registered.
- // TODO: consider blocking boot on registration and simplify much of the logic in this class
- IBinder connector;
- try {
- final long before = System.currentTimeMillis();
- while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
- Thread.sleep(20);
- if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
- Slog.e(TAG, "Timeout waiting for NetworkStack connector");
- return null;
- }
- }
- } catch (InterruptedException e) {
- Slog.e(TAG, "Error waiting for NetworkStack connector", e);
- return null;
- }
-
- return INetworkStackConnector.Stub.asInterface(connector);
- }
-
- private void requestConnector(@NonNull NetworkStackCallback request) {
- // TODO: PID check.
- final int caller = Binder.getCallingUid();
- if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
- // Don't even attempt to obtain the connector and give a nice error message
- throw new SecurityException(
- "Only the system server should try to bind to the network stack.");
- }
-
- if (!mNetworkStackStartRequested) {
- // The network stack is not being started in this process, e.g. this process is not
- // the system server. Get a remote connector registered by the system server.
- final INetworkStackConnector connector = getRemoteConnector();
- synchronized (mPendingNetStackRequests) {
- mConnector = connector;
- }
- request.onNetworkStackConnected(connector);
- return;
- }
-
- final INetworkStackConnector connector;
- synchronized (mPendingNetStackRequests) {
- connector = mConnector;
- if (connector == null) {
- mPendingNetStackRequests.add(request);
- return;
- }
- }
-
- request.onNetworkStackConnected(connector);
- }
+ private NetworkStack() {}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index ab6dd7c..b7e65b9 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -46,6 +46,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -262,6 +263,7 @@
private static final long BYTES_PER_KB = 1024;
private static final long BYTES_PER_MB = 1048576; // 1024^2
private static final long BYTES_PER_GB = 1073741824; //1024^3
+ public static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
private static final String VERSION_DATA = "vers";
private static final String UID_DATA = "uid";
@@ -482,6 +484,13 @@
* yield a value of 0 if the device doesn't support power calculations.
*/
public abstract LongCounter getPowerCounter();
+
+ /**
+ * @return a non-null {@link LongCounter} representing total power monitored on the rails
+ * in mAms (miliamps-milliseconds). The counter may always yield a value of 0 if the device
+ * doesn't support power rail monitoring.
+ */
+ public abstract LongCounter getMonitoredRailChargeConsumedMaMs();
}
/**
@@ -1526,6 +1535,9 @@
// The charge of the battery in micro-Ampere-hours.
public int batteryChargeUAh;
+ public double modemRailChargeMah;
+ public double wifiRailChargeMah;
+
// Constants from SCREEN_BRIGHTNESS_*
public static final int STATE_BRIGHTNESS_SHIFT = 0;
public static final int STATE_BRIGHTNESS_MASK = 0x7;
@@ -1738,6 +1750,8 @@
| ((((int)batteryVoltage)<<16)&0xffff0000);
dest.writeInt(bat);
dest.writeInt(batteryChargeUAh);
+ dest.writeDouble(modemRailChargeMah);
+ dest.writeDouble(wifiRailChargeMah);
dest.writeInt(states);
dest.writeInt(states2);
if (wakelockTag != null) {
@@ -1767,6 +1781,8 @@
batteryTemperature = (short)(bat2&0xffff);
batteryVoltage = (char)((bat2>>16)&0xffff);
batteryChargeUAh = src.readInt();
+ modemRailChargeMah = src.readDouble();
+ wifiRailChargeMah = src.readDouble();
states = src.readInt();
states2 = src.readInt();
if ((bat&0x10000000) != 0) {
@@ -1807,6 +1823,8 @@
batteryTemperature = 0;
batteryVoltage = 0;
batteryChargeUAh = 0;
+ modemRailChargeMah = 0;
+ wifiRailChargeMah = 0;
states = 0;
states2 = 0;
wakelockTag = null;
@@ -1835,6 +1853,8 @@
batteryTemperature = o.batteryTemperature;
batteryVoltage = o.batteryVoltage;
batteryChargeUAh = o.batteryChargeUAh;
+ modemRailChargeMah = o.modemRailChargeMah;
+ wifiRailChargeMah = o.wifiRailChargeMah;
states = o.states;
states2 = o.states2;
if (o.wakelockTag != null) {
@@ -1867,6 +1887,8 @@
&& batteryTemperature == o.batteryTemperature
&& batteryVoltage == o.batteryVoltage
&& batteryChargeUAh == o.batteryChargeUAh
+ && modemRailChargeMah == o.modemRailChargeMah
+ && wifiRailChargeMah == o.wifiRailChargeMah
&& states == o.states
&& states2 == o.states2
&& currentTime == o.currentTime;
@@ -3311,7 +3333,8 @@
if (counter.getIdleTimeCounter().getCountLocked(which) != 0
|| counter.getRxTimeCounter().getCountLocked(which) != 0
- || counter.getPowerCounter().getCountLocked(which) != 0) {
+ || counter.getPowerCounter().getCountLocked(which) != 0
+ || counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) != 0) {
return true;
}
@@ -3345,7 +3368,10 @@
pw.print(",");
pw.print(counter.getRxTimeCounter().getCountLocked(which));
pw.print(",");
- pw.print(counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+ pw.print(counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+ pw.print(",");
+ pw.print(counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+ / (MILLISECONDS_IN_HOUR));
for (LongCounter c : counter.getTxTimeCounters()) {
pw.print(",");
pw.print(c.getCountLocked(which));
@@ -3370,7 +3396,10 @@
proto.write(ControllerActivityProto.RX_DURATION_MS,
counter.getRxTimeCounter().getCountLocked(which));
proto.write(ControllerActivityProto.POWER_MAH,
- counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+ counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+ proto.write(ControllerActivityProto.MONITORED_RAIL_CHARGE_MAH,
+ counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+ / (MILLISECONDS_IN_HOUR));
long tToken;
LongCounter[] txCounters = counter.getTxTimeCounters();
@@ -3400,6 +3429,8 @@
final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which);
+ final long monitoredRailChargeConsumedMaMs =
+ counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
// Battery real time
final long totalControllerActivityTimeMs
= computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
@@ -3522,10 +3553,22 @@
sb.append(" ");
sb.append(controllerName);
sb.append(" Battery drain: ").append(
- BatteryStatsHelper.makemAh(powerDrainMaMs / (double) (1000*60*60)));
+ BatteryStatsHelper.makemAh(powerDrainMaMs / MILLISECONDS_IN_HOUR));
sb.append("mAh");
pw.println(sb.toString());
}
+
+ if (monitoredRailChargeConsumedMaMs > 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(controllerName);
+ sb.append(" Monitored rail energy drain: ").append(
+ new DecimalFormat("#.##").format(
+ monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+ }
}
/**
@@ -6103,6 +6146,8 @@
int oldTemp = -1;
int oldVolt = -1;
int oldChargeMAh = -1;
+ double oldModemRailChargeMah = -1;
+ double oldWifiRailChargeMah = -1;
long lastTime = -1;
void reset() {
@@ -6114,6 +6159,8 @@
oldTemp = -1;
oldVolt = -1;
oldChargeMAh = -1;
+ oldModemRailChargeMah = -1;
+ oldWifiRailChargeMah = -1;
}
public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
@@ -6299,6 +6346,16 @@
item.append(checkin ? ",Bcc=" : " charge=");
item.append(oldChargeMAh);
}
+ if (oldModemRailChargeMah != rec.modemRailChargeMah) {
+ oldModemRailChargeMah = rec.modemRailChargeMah;
+ item.append(checkin ? ",Mrc=" : " modemRailChargemAh=");
+ item.append(new DecimalFormat("#.##").format(oldModemRailChargeMah));
+ }
+ if (oldWifiRailChargeMah != rec.wifiRailChargeMah) {
+ oldWifiRailChargeMah = rec.wifiRailChargeMah;
+ item.append(checkin ? ",Wrc=" : " wifiRailChargemAh=");
+ item.append(new DecimalFormat("#.##").format(oldWifiRailChargeMah));
+ }
printBitDescriptions(item, oldState, rec.states, rec.wakelockTag,
HISTORY_STATE_DESCRIPTIONS, !checkin);
printBitDescriptions(item, oldState2, rec.states2, null,
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 87e1b7d..1420e2f 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.SystemApi;
import android.content.LocaleProto;
import android.icu.util.ULocale;
import android.util.proto.ProtoOutputStream;
@@ -324,6 +325,15 @@
return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
}
+ /**
+ * Returns true if locale is a pseudo-locale, false otherwise.
+ * {@hide}
+ */
+ @SystemApi
+ public static boolean isPseudoLocale(@Nullable ULocale locale) {
+ return isPseudoLocale(locale != null ? locale.toLocale() : null);
+ }
+
@IntRange(from=0, to=1)
private static int matchScore(Locale supported, Locale desired) {
if (supported.equals(desired)) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2ecf9d1..cfe2d28 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1784,7 +1784,7 @@
*
* see {@link #registerThermalStatusCallback}
*/
- public void unregisterThermalStatusCallback(ThermalStatusCallback callback) {
+ public void unregisterThermalStatusCallback(@NonNull ThermalStatusCallback callback) {
Preconditions.checkNotNull(callback, "callback cannnot be null");
synchronized (this) {
if (mThermalService == null) {
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
index e5341ee..3639c71 100644
--- a/core/java/android/os/connectivity/WifiBatteryStats.java
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -44,6 +44,7 @@
private long[] mTimeInStateMs;
private long[] mTimeInSupplicantStateMs;
private long[] mTimeInRxSignalStrengthLevelMs;
+ private long mMonitoredRailChargeConsumedMaMs;
public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new
Parcelable.Creator<WifiBatteryStats>() {
@@ -77,6 +78,7 @@
out.writeLongArray(mTimeInStateMs);
out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
out.writeLongArray(mTimeInSupplicantStateMs);
+ out.writeLong(mMonitoredRailChargeConsumedMaMs);
}
public void readFromParcel(Parcel in) {
@@ -96,6 +98,7 @@
in.readLongArray(mTimeInStateMs);
in.readLongArray(mTimeInRxSignalStrengthLevelMs);
in.readLongArray(mTimeInSupplicantStateMs);
+ mMonitoredRailChargeConsumedMaMs = in.readLong();
}
public long getLoggingDurationMs() {
@@ -162,6 +165,10 @@
return mTimeInSupplicantStateMs;
}
+ public long getMonitoredRailChargeConsumedMaMs() {
+ return mMonitoredRailChargeConsumedMaMs;
+ }
+
public void setLoggingDurationMs(long t) {
mLoggingDurationMs = t;
return;
@@ -245,6 +252,11 @@
return;
}
+ public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) {
+ mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs;
+ return;
+ }
+
public int describeContents() {
return 0;
}
@@ -274,6 +286,7 @@
Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES];
Arrays.fill(mTimeInSupplicantStateMs, 0);
+ mMonitoredRailChargeConsumedMaMs = 0;
return;
}
}
\ No newline at end of file
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index d2f9859..ae70775 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -49,7 +49,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* A service used to capture the content of the screen to provide contextual data in other areas of
@@ -164,17 +166,9 @@
}
/**
- * Explicitly limits content capture to the given packages and activities.
- *
- * <p>To reset the whitelist, call it passing {@code null} to both arguments.
- *
- * <p>Useful when the service wants to restrict content capture to a category of apps, like
- * chat apps. For example, if the service wants to support view captures on all activities of
- * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
- * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
- * Arrays.asList(new ComponentName("ChatApp2", "act1"),
- * new ComponentName("ChatApp2", "act2")));}
+ * @deprecated use {@link #setContentCaptureWhitelist(Set, Set)} instead
*/
+ @Deprecated
public final void setContentCaptureWhitelist(@Nullable List<String> packages,
@Nullable List<ComponentName> activities) {
final IContentCaptureServiceCallback callback = mCallback;
@@ -190,6 +184,27 @@
}
/**
+ * Explicitly limits content capture to the given packages and activities.
+ *
+ * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+ *
+ * <p>Useful when the service wants to restrict content capture to a category of apps, like
+ * chat apps. For example, if the service wants to support view captures on all activities of
+ * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
+ * it would call: {@code setContentCaptureWhitelist(Sets.newArraySet("ChatApp1"),
+ * Sets.newArraySet(new ComponentName("ChatApp2", "act1"),
+ * new ComponentName("ChatApp2", "act2")));}
+ */
+ public final void setContentCaptureWhitelist(@Nullable Set<String> packages,
+ @Nullable Set<ComponentName> activities) {
+ setContentCaptureWhitelist(toList(packages), toList(activities));
+ }
+
+ private <T> ArrayList<T> toList(@Nullable Set<T> set) {
+ return set == null ? null : new ArrayList<T>(set);
+ }
+
+ /**
* Called when the Android system connects to service.
*
* <p>You should generally do initialization here rather than in {@link #onCreate}.
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index ea2a25d..e3e63e5 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -342,37 +342,17 @@
}
/**
- * Requests that the voice state UI indicate the given state.
+ * Provide hints to be reflected in the system UI.
*
- * @param state value indicating whether the assistant is listening, fulfilling, etc.
+ * @param hints Arguments used to show UI.
*/
- public final void setVoiceState(int state) {
- try {
- mSystemService.setVoiceState(mInterface, state);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ public final void setUiHints(@NonNull Bundle hints) {
+ if (hints == null) {
+ throw new IllegalArgumentException("Hints must be non-null");
}
- }
- /**
- * Displays the given voice transcription contents.
- */
- public final void setTranscription(@NonNull String transcription) {
try {
- mSystemService.setTranscription(mInterface, transcription);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Hides transcription.
- *
- * @param immediate if {@code true}, remove before transcription animation completes.
- */
- public final void clearTranscription(boolean immediate) {
- try {
- mSystemService.clearTranscription(mInterface, immediate);
+ mSystemService.setUiHints(mInterface, hints);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 1dbc46b..6cfc9f2 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -25,6 +25,7 @@
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.os.Build;
+import android.os.Handler;
import android.util.SparseIntArray;
import com.android.internal.util.VirtualRefBasePtr;
@@ -84,6 +85,7 @@
private VirtualRefBasePtr mNativePtr;
+ private Handler mHandler;
private RenderNode mTarget;
private View mViewTarget;
private int mRenderProperty = -1;
@@ -222,6 +224,9 @@
private void moveToRunningState() {
mState = STATE_RUNNING;
if (mNativePtr != null) {
+ if (mHandler == null) {
+ mHandler = new Handler();
+ }
nStart(mNativePtr.get());
}
notifyStartListeners();
@@ -497,7 +502,7 @@
// Called by native
@UnsupportedAppUsage
private static void callOnFinished(RenderNodeAnimator animator) {
- animator.onFinished();
+ animator.mHandler.post(animator::onFinished);
}
@Override
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 47b206ca..2097812 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -453,7 +453,7 @@
*/
void destroyHardwareResources(View view) {
destroyResources(view);
- destroyHardwareResources();
+ clearContent();
}
private static void destroyResources(View view) {
@@ -735,7 +735,9 @@
if (callback != null) {
setFrameCallback(callback);
}
- syncAndDrawFrame(vsync);
+ createRenderRequest()
+ .setVsyncTime(vsync)
+ .syncAndDraw();
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1a782ee..b1fee2d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3404,21 +3404,25 @@
.captureFrameCommitCallbacks();
if (mReportNextDraw) {
usingAsyncReport = true;
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
- // TODO: Use the frame number
- pendingDrawFinished();
- if (commitCallbacks != null) {
- for (int i = 0; i < commitCallbacks.size(); i++) {
- commitCallbacks.get(i).run();
- }
- }
- });
+ final Handler handler = mAttachInfo.mHandler;
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
+ handler.post(() -> {
+ // TODO: Use the frame number
+ pendingDrawFinished();
+ if (commitCallbacks != null) {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ }
+ }));
} else if (commitCallbacks != null && commitCallbacks.size() > 0) {
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
- for (int i = 0; i < commitCallbacks.size(); i++) {
- commitCallbacks.get(i).run();
- }
- });
+ final Handler handler = mAttachInfo.mHandler;
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
+ handler.post(() -> {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ }));
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index f93ac4c..a6b40ed 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -78,6 +78,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -1780,6 +1781,18 @@
}
/**
+ * @deprecated use {@link #setAugmentedAutofillWhitelist(Set, Set)} instead.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @Deprecated
+ public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
+ @Nullable List<ComponentName> activities) {
+ // TODO(b/123100824): implement
+ }
+
+ /**
* Explicitly limits augmented autofill to the given packages and activities.
*
* <p>To reset the whitelist, call it passing {@code null} to both arguments.
@@ -1799,8 +1812,8 @@
*/
@SystemApi
@TestApi
- public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
- @Nullable List<ComponentName> activities) {
+ public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
+ @Nullable Set<ComponentName> activities) {
// TODO(b/123100824): implement
}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 904a862..89e205c 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1230,9 +1230,16 @@
* Ensures that the drop down is not obscuring the IME.
* @param visible whether the ime should be in front. If false, the ime is pushed to
* the background.
+ *
+ * This method is deprecated. Please use the following methods instead.
+ * Use {@link #setInputMethodMode} to ensure that the drop down is not obscuring the IME.
+ * Use {@link #showDropDown()} to show the drop down immediately
+ * A combination of {@link #isDropDownAlwaysVisible()} and {@link #enoughToFilter()} to decide
+ * whether to manually trigger {@link #showDropDown()} or not.
+ *
* @hide internal used only here and SearchDialog
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768913)
public void ensureImeVisible(boolean visible) {
mPopup.setInputMethodMode(visible
? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
@@ -1242,14 +1249,39 @@
}
/**
- * @hide internal used only here and SearchDialog
+ * This method is deprecated. Please use {@link #getInputMethodMode()} instead.
+ *
+ * @hide This API is not being used and can be removed.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public boolean isInputMethodNotNeeded() {
return mPopup.getInputMethodMode() == ListPopupWindow.INPUT_METHOD_NOT_NEEDED;
}
/**
+ * Returns the input method mode used by the auto complete dropdown.
+ */
+ public int getInputMethodMode() {
+ return mPopup.getInputMethodMode();
+ }
+
+ /**
+ * Use this method to specify when the IME should be displayed. This function can be used to
+ * prevent the dropdown from obscuring the IME.
+ *
+ * @param mode speficies the input method mode. use one of the following values:
+ *
+ * {@link ListPopupWindow#INPUT_METHOD_FROM_FOCUSABLE} IME Displayed if the auto-complete box is
+ * focusable.
+ * {@link ListPopupWindow#INPUT_METHOD_NEEDED} Always display the IME.
+ * {@link ListPopupWindow#INPUT_METHOD_NOT_NEEDED}. The auto-complete suggestions are always
+ * displayed, even if the suggestions cover/hide the input method.
+ */
+ public void setInputMethodMode(int mode) {
+ mPopup.setInputMethodMode(mode);
+ }
+
+ /**
* <p>Displays the drop down on screen.</p>
*/
public void showDropDown() {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 29f070e..8113b40 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -199,7 +199,9 @@
private boolean mMaxInitialized;
private int mBehavior;
- @UnsupportedAppUsage
+ // Better to define a Drawable that implements Animatable if you want to modify animation
+ // characteristics programatically.
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052713)
private int mDuration;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private boolean mIndeterminate;
@@ -284,7 +286,6 @@
}
}
-
mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);
mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);
@@ -709,7 +710,17 @@
/**
* Define the drawable used to draw the progress bar in indeterminate mode.
*
+ * <p>For the Drawable to animate, it must implement {@link Animatable}, or override
+ * {@link Drawable#onLevelChange(int)}. A Drawable that implements Animatable will be animated
+ * via that interface and therefore provides the greatest amount of customization. A Drawable
+ * that only overrides onLevelChange(int) is animated directly by ProgressBar and only the
+ * animation {@link android.R.styleable#ProgressBar_indeterminateDuration duration},
+ * {@link android.R.styleable#ProgressBar_indeterminateBehavior repeating behavior}, and
+ * {@link #setInterpolator(Interpolator) interpolator} can be modified, and only before the
+ * indeterminate animation begins.
+ *
* @param d the new drawable
+ * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable
* @see #getIndeterminateDrawable()
* @see #setIndeterminate(boolean)
*/
@@ -1774,10 +1785,21 @@
/**
* Sets the acceleration curve for the indeterminate animation.
- * The interpolator is loaded as a resource from the specified context.
+ *
+ * <p>The interpolator is loaded as a resource from the specified context. Defaults to a linear
+ * interpolation.
+ *
+ * <p>The interpolator only affects the indeterminate animation if the
+ * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+ * implement {@link Animatable}.
+ *
+ * <p>This call must be made before the indeterminate animation starts for it to have an affect.
*
* @param context The application environment
* @param resID The resource identifier of the interpolator to load
+ * @attr ref android.R.styleable#ProgressBar_interpolator
+ * @see #setInterpolator(Interpolator)
+ * @see #getInterpolator()
*/
public void setInterpolator(Context context, @InterpolatorRes int resID) {
setInterpolator(AnimationUtils.loadInterpolator(context, resID));
@@ -1787,7 +1809,17 @@
* Sets the acceleration curve for the indeterminate animation.
* Defaults to a linear interpolation.
*
+ * <p>The interpolator only affects the indeterminate animation if the
+ * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+ * implement {@link Animatable}.
+ *
+ * <p>This call must be made before the indeterminate animation starts for it to have
+ * an affect.
+ *
* @param interpolator The interpolator which defines the acceleration curve
+ * @attr ref android.R.styleable#ProgressBar_interpolator
+ * @see #setInterpolator(Context, int)
+ * @see #getInterpolator()
*/
public void setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
@@ -1797,6 +1829,9 @@
* Gets the acceleration curve type for the indeterminate animation.
*
* @return the {@link Interpolator} associated to this animation
+ * @attr ref android.R.styleable#ProgressBar_interpolator
+ * @see #setInterpolator(Context, int)
+ * @see #setInterpolator(Interpolator)
*/
@InspectableProperty
public Interpolator getInterpolator() {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8ebcef5..2009fd50 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -46,6 +46,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
+import android.content.res.Configuration;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
@@ -486,6 +487,27 @@
}
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ int width = -1;
+ if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width);
+ }
+
+ updateLayoutWidth(R.id.content_preview_text_layout, width);
+ updateLayoutWidth(R.id.content_preview_title_layout, width);
+ updateLayoutWidth(R.id.content_preview_file_layout, width);
+ }
+
+ private void updateLayoutWidth(int layoutResourceId, int width) {
+ View view = findViewById(layoutResourceId);
+ LayoutParams params = view.getLayoutParams();
+ params.width = width;
+ view.setLayoutParams(params);
+ }
+
private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) {
switch (previewType) {
case CONTENT_PREVIEW_TEXT:
@@ -601,7 +623,14 @@
private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
String fileName = null;
boolean hasThumbnail = false;
- Cursor cursor = resolver.query(uri, null, null, null, null);
+ Cursor cursor = null;
+
+ try {
+ cursor = resolver.query(uri, null, null, null, null);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Error loading file preview", e);
+ }
+
if (cursor != null && cursor.getCount() > 0) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);
@@ -633,40 +662,50 @@
// due to permissions issues
findViewById(R.id.file_copy_button).setVisibility(View.GONE);
- ContentResolver resolver = getContentResolver();
- TextView fileNameView = findViewById(R.id.content_preview_filename);
String action = targetIntent.getAction();
if (Intent.ACTION_SEND.equals(action)) {
Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
-
- FileInfo fileInfo = extractFileInfo(uri, resolver);
- fileNameView.setText(fileInfo.name);
-
- if (fileInfo.hasThumbnail) {
- loadUriIntoView(R.id.content_preview_file_thumbnail, uri);
- } else {
- ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
- fileIconView.setVisibility(View.VISIBLE);
- fileIconView.setImageResource(R.drawable.ic_doc_generic);
- }
+ loadFileUriIntoView(uri);
} else {
List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
- if (uris.size() == 0) {
+ int uriCount = uris.size();
+
+ if (uriCount == 0) {
contentPreviewLayout.setVisibility(View.GONE);
Log.i(TAG,
- "Appears to be no uris available in EXTRA_STREAM, removing preview area");
+ "Appears to be no uris available in EXTRA_STREAM, removing "
+ + "preview area");
return;
+ } else if (uriCount == 1) {
+ loadFileUriIntoView(uris.get(0));
+ } else {
+ FileInfo fileInfo = extractFileInfo(uris.get(0), getContentResolver());
+ int remUriCount = uriCount - 1;
+ String fileName = getResources().getQuantityString(R.plurals.file_count,
+ remUriCount, fileInfo.name, remUriCount);
+
+ TextView fileNameView = findViewById(R.id.content_preview_filename);
+ fileNameView.setText(fileName);
+
+ ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
+ fileIconView.setVisibility(View.VISIBLE);
+ fileIconView.setImageResource(R.drawable.ic_file_copy);
}
+ }
+ }
- FileInfo fileInfo = extractFileInfo(uris.get(0), resolver);
- int remFileCount = uris.size() - 1;
- String fileName = getResources().getQuantityString(R.plurals.file_count,
- remFileCount, fileInfo.name, remFileCount);
+ private void loadFileUriIntoView(Uri uri) {
+ FileInfo fileInfo = extractFileInfo(uri, getContentResolver());
- fileNameView.setText(fileName);
+ TextView fileNameView = findViewById(R.id.content_preview_filename);
+ fileNameView.setText(fileInfo.name);
+
+ if (fileInfo.hasThumbnail) {
+ loadUriIntoView(R.id.content_preview_file_thumbnail, uri);
+ } else {
ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
fileIconView.setVisibility(View.VISIBLE);
- fileIconView.setImageResource(R.drawable.ic_file_copy);
+ fileIconView.setImageResource(R.drawable.ic_doc_generic);
}
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 8dde44e..9ce7ed1 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -153,17 +153,7 @@
in IVoiceActionCheckCallback callback);
/**
- * Sets the transcribed voice to the given string.
+ * Provide hints for showing UI.
*/
- void setTranscription(IVoiceInteractionService service, String transcription);
-
- /**
- * Indicates that the transcription session is finished.
- */
- void clearTranscription(IVoiceInteractionService service, boolean immediate);
-
- /**
- * Sets the voice state indication based upon the given value.
- */
- void setVoiceState(IVoiceInteractionService service, int state);
+ void setUiHints(in IVoiceInteractionService service, in Bundle hints);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
index 674ad5b..bc757e2 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
@@ -16,6 +16,8 @@
package com.android.internal.app;
+ import android.os.Bundle;
+
oneway interface IVoiceInteractionSessionListener {
/**
* Called when a voice session is shown.
@@ -28,18 +30,7 @@
void onVoiceSessionHidden();
/**
- * Called when voice assistant transcription has been updated to the given string.
+ * Called when UI hints were received.
*/
- void onTranscriptionUpdate(in String transcription);
-
- /**
- * Called when voice transcription is completed.
- */
- void onTranscriptionComplete(in boolean immediate);
-
- /**
- * Called when the voice assistant's state has changed. Values are from
- * VoiceInteractionService's VOICE_STATE* constants.
- */
- void onVoiceStateChange(in int state);
+ void onSetUiHints(in Bundle args);
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 52e1748..4ff9948 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -187,6 +187,8 @@
static final int MSG_REPORT_RESET_STATS = 4;
static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
+ private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
+
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
@@ -252,6 +254,9 @@
private static final long RPM_STATS_UPDATE_FREQ_MS = 1000;
/** Last time that RPM stats were updated by updateRpmStatsLocked. */
private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
+
+ /** Container for Rail Energy Data stats. */
+ private final RailStats mTmpRailStats = new RailStats();
/**
* Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
* {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
@@ -327,6 +332,15 @@
public String getSubsystemLowPowerStats();
}
+ /** interface to update rail information for power monitor */
+ public interface RailEnergyDataCallback {
+ /** Function to fill the map for the rail data stats
+ * Used for power monitoring feature
+ * @param railStats
+ */
+ void fillRailDataStats(RailStats railStats);
+ }
+
public static abstract class UserInfoProvider {
private int[] userIds;
protected abstract @Nullable int[] getUserIds();
@@ -361,6 +375,8 @@
}
};
+ public final RailEnergyDataCallback mRailEnergyDataCallback;
+
/**
* This handler is running on {@link BackgroundThread}.
*/
@@ -593,7 +609,9 @@
int UPDATE_RADIO = 0x04;
int UPDATE_BT = 0x08;
int UPDATE_RPM = 0x10; // 16
- int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM;
+ int UPDATE_RAIL = 0x20; // 32
+ int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM
+ | UPDATE_RAIL;
Future<?> scheduleSync(String reason, int flags);
Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
@@ -1078,6 +1096,7 @@
mBatteryStatsHistory = null;
mHandler = null;
mPlatformIdleStateCallback = null;
+ mRailEnergyDataCallback = null;
mUserInfoProvider = null;
mConstants = new Constants(mHandler);
clearHistoryLocked();
@@ -3005,6 +3024,7 @@
private final LongSamplingCounter mRxTimeMillis;
private final LongSamplingCounter[] mTxTimeMillis;
private final LongSamplingCounter mPowerDrainMaMs;
+ private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs;
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
mIdleTimeMillis = new LongSamplingCounter(timeBase);
@@ -3016,6 +3036,7 @@
mTxTimeMillis[i] = new LongSamplingCounter(timeBase);
}
mPowerDrainMaMs = new LongSamplingCounter(timeBase);
+ mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase);
}
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
@@ -3033,6 +3054,7 @@
mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in);
}
mPowerDrainMaMs = new LongSamplingCounter(timeBase, in);
+ mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in);
}
public void readSummaryFromParcel(Parcel in) {
@@ -3048,6 +3070,7 @@
counter.readSummaryFromParcelLocked(in);
}
mPowerDrainMaMs.readSummaryFromParcelLocked(in);
+ mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in);
}
@Override
@@ -3065,6 +3088,7 @@
counter.writeSummaryFromParcelLocked(dest);
}
mPowerDrainMaMs.writeSummaryFromParcelLocked(dest);
+ mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest);
}
@Override
@@ -3078,6 +3102,7 @@
counter.writeToParcel(dest);
}
mPowerDrainMaMs.writeToParcel(dest);
+ mMonitoredRailChargeConsumedMaMs.writeToParcel(dest);
}
public void reset(boolean detachIfReset) {
@@ -3089,6 +3114,7 @@
counter.reset(detachIfReset);
}
mPowerDrainMaMs.reset(detachIfReset);
+ mMonitoredRailChargeConsumedMaMs.reset(detachIfReset);
}
public void detach() {
@@ -3100,6 +3126,7 @@
counter.detach();
}
mPowerDrainMaMs.detach();
+ mMonitoredRailChargeConsumedMaMs.detach();
}
/**
@@ -3154,6 +3181,15 @@
public LongSamplingCounter getPowerCounter() {
return mPowerDrainMaMs;
}
+
+ /**
+ * @return a LongSamplingCounter, measuring actual monitored rail energy consumed
+ * milli-ampere milli-seconds (mAmS).
+ */
+ @Override
+ public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() {
+ return mMonitoredRailChargeConsumedMaMs;
+ }
}
/** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
@@ -3497,6 +3533,8 @@
if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUAh=" + cur.batteryChargeUAh);
dest.writeInt(cur.batteryChargeUAh);
}
+ dest.writeDouble(cur.modemRailChargeMah);
+ dest.writeDouble(cur.wifiRailChargeMah);
}
private int buildBatteryLevelInt(HistoryItem h) {
@@ -3747,6 +3785,8 @@
if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) {
cur.batteryChargeUAh = src.readInt();
}
+ cur.modemRailChargeMah = src.readDouble();
+ cur.wifiRailChargeMah = src.readDouble();
}
@Override
@@ -10111,12 +10151,12 @@
}
public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
- UserInfoProvider userInfoProvider) {
- this(new SystemClocks(), systemDir, handler, cb, userInfoProvider);
+ RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) {
+ this(new SystemClocks(), systemDir, handler, cb, railStatsCb, userInfoProvider);
}
private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
- PlatformIdleStateCallback cb,
+ PlatformIdleStateCallback cb, RailEnergyDataCallback railStatsCb,
UserInfoProvider userInfoProvider) {
init(clocks);
@@ -10218,6 +10258,7 @@
clearHistoryLocked();
updateDailyDeadlineLocked();
mPlatformIdleStateCallback = cb;
+ mRailEnergyDataCallback = railStatsCb;
mUserInfoProvider = userInfoProvider;
}
@@ -10238,6 +10279,7 @@
mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
readFromParcel(p);
mPlatformIdleStateCallback = null;
+ mRailEnergyDataCallback = null;
}
public void setPowerProfileLocked(PowerProfile profile) {
@@ -10934,6 +10976,8 @@
mWakeupReasonStats.clear();
}
+ mTmpRailStats.reset();
+
mLastHistoryStepDetails = null;
mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
@@ -11321,6 +11365,16 @@
mWifiActivity.getPowerCounter().addCountLocked(
(long) (info.getControllerEnergyUsed() / opVolt));
}
+ // Converting uWs to mAms.
+ // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
+ long monitoredRailChargeConsumedMaMs =
+ (long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt);
+ mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
+ monitoredRailChargeConsumedMaMs);
+ mHistoryCur.wifiRailChargeMah +=
+ (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+ addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mTmpRailStats.resetWifiTotalEnergyUsed();
}
}
}
@@ -11411,9 +11465,18 @@
// We store the power drain as mAms.
mModemActivity.getPowerCounter().addCountLocked((long) energyUsed);
+ // Converting uWs to mAms.
+ // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
+ long monitoredRailChargeConsumedMaMs =
+ (long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt);
+ mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
+ monitoredRailChargeConsumedMaMs);
+ mHistoryCur.modemRailChargeMah +=
+ (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+ addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mTmpRailStats.resetCellularTotalEnergyUsed();
}
}
-
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000);
@@ -11812,6 +11875,16 @@
}
/**
+ * Read and record Rail Energy data.
+ */
+ public void updateRailStatsLocked() {
+ if (mRailEnergyDataCallback == null || !mTmpRailStats.isRailStatsAvailable()) {
+ return;
+ }
+ mRailEnergyDataCallback.fillRailDataStats(mTmpRailStats);
+ }
+
+ /**
* Read and distribute kernel wake lock use across apps.
*/
public void updateKernelWakelocksLocked() {
@@ -12950,6 +13023,8 @@
final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+ final long monitoredRailChargeConsumedMaMs =
+ counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
long[] timeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES];
for (int i = 0; i < timeInRatMs.length; i++) {
timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTime, which) / 1000;
@@ -12979,58 +13054,62 @@
s.setTimeInRatMs(timeInRatMs);
s.setTimeInRxSignalStrengthLevelMs(timeInRxSignalStrengthLevelMs);
s.setTxTimeMs(txTimeMs);
+ s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs);
return s;
}
- /*@hide */
- public WifiBatteryStats getWifiBatteryStats() {
- WifiBatteryStats s = new WifiBatteryStats();
- final int which = STATS_SINCE_CHARGED;
- final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
- final ControllerActivityCounter counter = getWifiControllerActivity();
- final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
- final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
- final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
- final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
- final long totalControllerActivityTimeMs
- = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
- final long sleepTimeMs
- = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
- final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
- long numAppScanRequest = 0;
- for (int i = 0; i < mUidStats.size(); i++) {
- numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
- }
- long[] timeInStateMs = new long[NUM_WIFI_STATES];
- for (int i=0; i<NUM_WIFI_STATES; i++) {
+ /*@hide */
+ public WifiBatteryStats getWifiBatteryStats() {
+ WifiBatteryStats s = new WifiBatteryStats();
+ final int which = STATS_SINCE_CHARGED;
+ final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+ final ControllerActivityCounter counter = getWifiControllerActivity();
+ final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+ final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+ final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+ final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
+ final long totalControllerActivityTimeMs
+ = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
+ final long sleepTimeMs
+ = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
+ final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+ final long monitoredRailChargeConsumedMaMs =
+ counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
+ long numAppScanRequest = 0;
+ for (int i = 0; i < mUidStats.size(); i++) {
+ numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
+ }
+ long[] timeInStateMs = new long[NUM_WIFI_STATES];
+ for (int i=0; i<NUM_WIFI_STATES; i++) {
timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000;
- }
- long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
- for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
- timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
- }
- long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
- for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
- timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
- }
- s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
- s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
- s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
- s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
- s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
- s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
- s.setSleepTimeMs(sleepTimeMs);
- s.setIdleTimeMs(idleTimeMs);
- s.setRxTimeMs(rxTimeMs);
- s.setTxTimeMs(txTimeMs);
- s.setScanTimeMs(scanTimeMs);
- s.setEnergyConsumedMaMs(energyConsumedMaMs);
- s.setNumAppScanRequest(numAppScanRequest);
- s.setTimeInStateMs(timeInStateMs);
- s.setTimeInSupplicantStateMs(timeInSupplStateMs);
- s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
- return s;
- }
+ }
+ long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
+ for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+ timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
+ }
+ long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+ for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+ timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
+ }
+ s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+ s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
+ s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+ s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+ s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+ s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+ s.setSleepTimeMs(sleepTimeMs);
+ s.setIdleTimeMs(idleTimeMs);
+ s.setRxTimeMs(rxTimeMs);
+ s.setTxTimeMs(txTimeMs);
+ s.setScanTimeMs(scanTimeMs);
+ s.setEnergyConsumedMaMs(energyConsumedMaMs);
+ s.setNumAppScanRequest(numAppScanRequest);
+ s.setTimeInStateMs(timeInStateMs);
+ s.setTimeInSupplicantStateMs(timeInSupplStateMs);
+ s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
+ s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs);
+ return s;
+ }
/*@hide */
public GpsBatteryStats getGpsBatteryStats() {
diff --git a/core/java/com/android/internal/os/RailStats.java b/core/java/com/android/internal/os/RailStats.java
new file mode 100644
index 0000000..ff00831
--- /dev/null
+++ b/core/java/com/android/internal/os/RailStats.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import java.util.Map;
+
+/** Rail Stats Power Monitoring Class */
+public final class RailStats {
+ private static final String TAG = "RailStats";
+
+ private static final String WIFI_SUBSYSTEM = "wifi";
+ private static final String CELLULAR_SUBSYSTEM = "cellular";
+
+ private Map<Long, RailInfoData> mRailInfoData = new ArrayMap<>();
+
+ private long mCellularTotalEnergyUseduWs = 0;
+ private long mWifiTotalEnergyUseduWs = 0;
+ private boolean mRailStatsAvailability = true;
+
+ /** Updates the rail data map of all power monitor rails being monitored
+ * Function is called from native side
+ * @param index
+ * @param railName
+ * @param subSystemName
+ * @param timestampSinceBootMs
+ * @param energyUsedSinceBootuWs
+ */
+ public void updateRailData(long index, String railName, String subSystemName,
+ long timestampSinceBootMs, long energyUsedSinceBootuWs) {
+ if (!(subSystemName.equals(WIFI_SUBSYSTEM) || subSystemName.equals(CELLULAR_SUBSYSTEM))) {
+ return;
+ }
+ RailInfoData node = mRailInfoData.get(index);
+ if (node == null) {
+ mRailInfoData.put(index, new RailInfoData(index, railName, subSystemName,
+ timestampSinceBootMs, energyUsedSinceBootuWs));
+ if (subSystemName.equals(WIFI_SUBSYSTEM)) {
+ mWifiTotalEnergyUseduWs += energyUsedSinceBootuWs;
+ return;
+ }
+ if (subSystemName.equals(CELLULAR_SUBSYSTEM)) {
+ mCellularTotalEnergyUseduWs += energyUsedSinceBootuWs;
+ }
+ return;
+ }
+ long timeSinceLastLogMs = timestampSinceBootMs - node.timestampSinceBootMs;
+ long energyUsedSinceLastLoguWs = energyUsedSinceBootuWs - node.energyUsedSinceBootuWs;
+ if (timeSinceLastLogMs < 0 || energyUsedSinceLastLoguWs < 0) {
+ energyUsedSinceLastLoguWs = node.energyUsedSinceBootuWs;
+ }
+ node.timestampSinceBootMs = timestampSinceBootMs;
+ node.energyUsedSinceBootuWs = energyUsedSinceBootuWs;
+ if (subSystemName.equals(WIFI_SUBSYSTEM)) {
+ mWifiTotalEnergyUseduWs += energyUsedSinceLastLoguWs;
+ return;
+ }
+ if (subSystemName.equals(CELLULAR_SUBSYSTEM)) {
+ mCellularTotalEnergyUseduWs += energyUsedSinceLastLoguWs;
+ }
+ }
+
+ /** resets the cellular total energy used aspect.
+ */
+ public void resetCellularTotalEnergyUsed() {
+ mCellularTotalEnergyUseduWs = 0;
+ }
+
+ /** resets the wifi total energy used aspect.
+ */
+ public void resetWifiTotalEnergyUsed() {
+ mWifiTotalEnergyUseduWs = 0;
+ }
+
+ public long getCellularTotalEnergyUseduWs() {
+ return mCellularTotalEnergyUseduWs;
+ }
+
+ public long getWifiTotalEnergyUseduWs() {
+ return mWifiTotalEnergyUseduWs;
+ }
+
+ /** reset the total energy subsystems
+ *
+ */
+ public void reset() {
+ mCellularTotalEnergyUseduWs = 0;
+ mWifiTotalEnergyUseduWs = 0;
+ }
+
+ public RailStats getRailStats() {
+ return this;
+ }
+
+ public void setRailStatsAvailability(boolean railStatsAvailability) {
+ mRailStatsAvailability = railStatsAvailability;
+ }
+
+ public boolean isRailStatsAvailable() {
+ return mRailStatsAvailability;
+ }
+
+ /** Container class to contain rail data information */
+ public static class RailInfoData {
+ private static final String TAG = "RailInfoData";
+ public long index;
+ public String railName;
+ public String subSystemName;
+ public long timestampSinceBootMs;
+ public long energyUsedSinceBootuWs;
+
+ private RailInfoData(long index, String railName, String subSystemName,
+ long timestampSinceBootMs, long energyUsedSinceBoot) {
+ this.index = index;
+ this.railName = railName;
+ this.subSystemName = subSystemName;
+ this.timestampSinceBootMs = timestampSinceBootMs;
+ this.energyUsedSinceBootuWs = energyUsedSinceBoot;
+ }
+
+ /** print the rail data
+ *
+ */
+ public void printData() {
+ Slog.d(TAG, "Index = " + index);
+ Slog.d(TAG, "RailName = " + railName);
+ Slog.d(TAG, "SubSystemName = " + subSystemName);
+ Slog.d(TAG, "TimestampSinceBootMs = " + timestampSinceBootMs);
+ Slog.d(TAG, "EnergyUsedSinceBootuWs = " + energyUsedSinceBootuWs);
+ }
+ }
+}
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 774c224..7ff15f2 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -6,4 +6,4 @@
per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
# Zygote
-per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
+per-file com_android_internal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 8c73630..2aa5cb4 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -33,7 +33,6 @@
#include <private/EGL/cache.h>
-#include <utils/Looper.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
@@ -144,52 +143,22 @@
uint32_t mRequestId;
};
-class RenderingException : public MessageHandler {
+class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
public:
- RenderingException(JavaVM* vm, const std::string& message)
- : mVm(vm)
- , mMessage(message) {
- }
-
- virtual void handleMessage(const Message&) {
- throwException(mVm, mMessage);
- }
-
- static void throwException(JavaVM* vm, const std::string& message) {
- JNIEnv* env = getenv(vm);
- jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
- }
-
-private:
- JavaVM* mVm;
- std::string mMessage;
-};
-
-class FrameCompleteWrapper : public MessageHandler {
-public:
- FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
- mLooper = Looper::getForThread();
- LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create runnable on a Looper thread!");
+ explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
mObject = env->NewGlobalRef(jobject);
LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
}
- virtual ~FrameCompleteWrapper() {
+ ~FrameCompleteWrapper() {
releaseObject();
}
- void postFrameComplete(int64_t frameNr) {
+ void onFrameComplete(int64_t frameNr) {
if (mObject) {
- mFrameNr = frameNr;
- mLooper->sendMessage(this, 0);
- }
- }
-
- virtual void handleMessage(const Message&) {
- if (mObject) {
- ATRACE_FORMAT("frameComplete %" PRId64, mFrameNr);
- getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, mFrameNr);
+ ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
+ getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
releaseObject();
}
}
@@ -197,8 +166,6 @@
private:
JavaVM* mVm;
jobject mObject;
- sp<Looper> mLooper;
- int64_t mFrameNr = -1;
void releaseObject() {
if (mObject) {
@@ -211,16 +178,14 @@
class RootRenderNode : public RenderNode, ErrorHandler {
public:
explicit RootRenderNode(JNIEnv* env) : RenderNode() {
- mLooper = Looper::getForThread();
- LOG_ALWAYS_FATAL_IF(!mLooper.get(),
- "Must create RootRenderNode on a thread with a looper!");
env->GetJavaVM(&mVm);
}
virtual ~RootRenderNode() {}
virtual void onError(const std::string& message) override {
- mLooper->sendMessage(new RenderingException(mVm, message), 0);
+ JNIEnv* env = getenv(mVm);
+ jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
}
virtual void prepareTree(TreeInfo& info) override {
@@ -249,14 +214,6 @@
info.errorHandler = nullptr;
}
- void sendMessage(const sp<MessageHandler>& handler) {
- mLooper->sendMessage(handler, 0);
- }
-
- void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) {
- mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0);
- }
-
void attachAnimatingNode(RenderNode* animatingNode) {
mPendingAnimatingRenderNodes.push_back(animatingNode);
}
@@ -404,7 +361,6 @@
}
private:
- sp<Looper> mLooper;
JavaVM* mVm;
std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
@@ -435,7 +391,9 @@
// the onFinished callback will then be ignored.
sp<FinishAndInvokeListener> message
= new FinishAndInvokeListener(anim);
- sendMessageDelayed(message, remainingTimeInMs);
+ auto looper = Looper::getForThread();
+ LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?");
+ looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0);
anim->clearOneShotListener();
}
}
@@ -463,7 +421,6 @@
virtual void runRemainingAnimations(TreeInfo& info) {
AnimationContext::runRemainingAnimations(info);
mRootNode->runVectorDrawableAnimators(this, info);
- postOnFinishedEvents();
}
virtual void pauseAnimators() override {
@@ -471,27 +428,16 @@
}
virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
- OnFinishedEvent event(animator, listener);
- mOnFinishedEvents.push_back(event);
+ listener->onAnimationFinished(animator);
}
virtual void destroy() {
AnimationContext::destroy();
mRootNode->detachAnimators();
- postOnFinishedEvents();
}
private:
sp<RootRenderNode> mRootNode;
- std::vector<OnFinishedEvent> mOnFinishedEvents;
-
- void postOnFinishedEvents() {
- if (mOnFinishedEvents.size()) {
- sp<InvokeAnimationListeners> message
- = new InvokeAnimationListeners(mOnFinishedEvents);
- mRootNode->sendMessage(message);
- }
- }
};
class ContextFactoryImpl : public IContextFactory {
@@ -958,7 +904,7 @@
} else {
sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
- wrapper->postFrameComplete(frameNr);
+ wrapper->onFrameComplete(frameNr);
});
}
}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index a4167c1..516fa7b 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -58,6 +58,10 @@
optional int64 duration_ms = 2;
}
repeated TxLevel tx = 4;
+
+ // Total rail charge consumed by the monitored rails by the controller. The value may
+ // always be 0 if the device doesn't support monitored rail calculations.
+ optional double monitored_rail_charge_mah = 5;
}
message SystemProto {
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 3683bfd..10798ad 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -20,12 +20,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:maxWidth="@dimen/resolver_max_width"
android:maxCollapsedHeight="288dp"
android:maxCollapsedHeightSmall="56dp"
android:id="@id/contentPanel">
-
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -153,8 +151,9 @@
android:background="?attr/colorBackgroundFloating">
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="@dimen/chooser_preview_width"
android:layout_height="wrap_content"
+ android:layout_gravity="center"
android:orientation="horizontal"
android:paddingLeft="@dimen/chooser_edge_margin_normal"
android:paddingRight="@dimen/chooser_edge_margin_normal"
@@ -182,8 +181,9 @@
<!-- Required sub-layout so we can get the nice rounded corners-->
<!-- around this section -->
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="@dimen/chooser_preview_width"
android:layout_height="wrap_content"
+ android:layout_gravity="center"
android:orientation="horizontal"
android:layout_marginLeft="@dimen/chooser_edge_margin_thin"
android:layout_marginRight="@dimen/chooser_edge_margin_thin"
@@ -224,8 +224,9 @@
android:background="?attr/colorBackgroundFloating">
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="@dimen/chooser_preview_width"
android:layout_height="wrap_content"
+ android:layout_gravity="center"
android:orientation="horizontal"
android:paddingLeft="@dimen/chooser_edge_margin_normal"
android:paddingRight="@dimen/chooser_edge_margin_normal"
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 351bd81..9e87a47 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -76,4 +76,6 @@
<!-- Floating toolbar dimensions -->
<dimen name="floating_toolbar_preferred_width">544dp</dimen>
+ <dimen name="chooser_preview_width">480dp</dimen>
+
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 46e14b4..224f54c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4349,14 +4349,18 @@
<attr name="indeterminate" format="boolean" />
<!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->
<attr name="indeterminateOnly" format="boolean" />
- <!-- Drawable used for the indeterminate mode. -->
+ <!-- Drawable used for the indeterminate mode. One that implements Animatable offers more
+ control over the animation.-->
<attr name="indeterminateDrawable" format="reference" />
<!-- Drawable used for the progress mode. -->
<attr name="progressDrawable" format="reference" />
- <!-- Duration of the indeterminate animation. -->
+ <!-- Duration of the indeterminate animation. Only affects the indeterminate animation
+ if the indeterminate Drawable does not implement
+ android.graphics.drawable.Animatable. -->
<attr name="indeterminateDuration" format="integer" min="1" />
- <!-- Defines how the indeterminate mode should behave when the progress
- reaches max. -->
+ <!-- Defines how the indeterminate mode should behave when the progress reaches max. Only
+ affects the indeterminate animation if the indeterminate Drawable does not implement
+ android.graphics.drawable.Animatable. -->
<attr name="indeterminateBehavior">
<!-- Progress starts over from 0. -->
<enum name="repeat" value="1" />
@@ -4367,6 +4371,9 @@
<attr name="maxWidth" />
<attr name="minHeight" format="dimension" />
<attr name="maxHeight" />
+ <!-- Sets the acceleration curve for the indeterminate animation. Defaults to a linear
+ interpolation. Only affects the indeterminate animation if the indeterminate Drawable
+ does not implement android.graphics.drawable.Animatable.-->
<attr name="interpolator" format="reference" />
<!-- Timeout between frames of animation in milliseconds.
{@deprecated Not used by the framework}. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dae2692..8dfd5e84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -46,6 +46,8 @@
<item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
@@ -55,8 +57,6 @@
<item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
</string-array>
@@ -3911,19 +3911,19 @@
<!-- See DisplayWhiteBalanceController.
A float array containing a list of ambient color temperatures, in Kelvin. This array,
- together with config_displayWhiteBalanceDisplayTemperatureValues, is used to generate a
+ together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
ambient color temperature readings to a target color temperature for the display.
This table is optional. If used, this array must,
1) Contain at least two entries
- 2) Be the same length as config_displayWhiteBalanceDisplayTemperatureValues. -->
- <array name="config_displayWhiteBalanceAmbientTemperatureValues">
+ 2) Be the same length as config_displayWhiteBalanceDisplayColorTemperatures. -->
+ <array name="config_displayWhiteBalanceAmbientColorTemperatures">
</array>
<!-- See DisplayWhiteBalanceController.
An array containing a list of display color temperatures, in Kelvin. See
- config_displayWhiteBalanceAmbientTemperatureValues for additional details.
+ config_displayWhiteBalanceAmbientColorTemperatures for additional details.
The same restrictions apply to this array. -->
- <array name="config_displayWhiteBalanceDisplayTemperatureValues">
+ <array name="config_displayWhiteBalanceDisplayColorTemperatures">
</array>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 9f86f84..39cbd26 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -721,4 +721,5 @@
<dimen name="chooser_edge_margin_thin">16dp</dimen>
<dimen name="chooser_edge_margin_normal">24dp</dimen>
<dimen name="chooser_preview_image_font_size">20sp</dimen>
+ <dimen name="chooser_preview_width">-1px</dimen>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0cdf388..5948f29 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3712,7 +3712,7 @@
<string name="ext_media_browse_action">Explore</string>
<!-- Notification action to transfer media [CHAR LIMIT=40] -->
- <string name="ext_media_seamless_action">Seamless transfer</string>
+ <string name="ext_media_seamless_action">Switch output</string>
<!-- Notification title when external media is missing [CHAR LIMIT=30] -->
<string name="ext_media_missing_title"><xliff:g id="name" example="SD card">%s</xliff:g> missing</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01abffe..16aed90 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2756,6 +2756,7 @@
<java-symbol type="dimen" name="chooser_edge_margin_thin" />
<java-symbol type="dimen" name="chooser_edge_margin_normal" />
<java-symbol type="dimen" name="chooser_preview_image_font_size"/>
+ <java-symbol type="dimen" name="chooser_preview_width" />
<java-symbol type="layout" name="chooser_grid" />
<java-symbol type="layout" name="resolve_grid_item" />
<java-symbol type="id" name="day_picker_view_pager" />
@@ -3652,7 +3653,7 @@
<java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
<java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" />
<java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
- <java-symbol type="array" name="config_displayWhiteBalanceAmbientTemperatureValues" />
- <java-symbol type="array" name="config_displayWhiteBalanceDisplayTemperatureValues" />
+ <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
+ <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
<java-symbol type="drawable" name="ic_action_open" />
</resources>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index bdb6364..8135671 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1509,6 +1509,9 @@
* Convenience method that returns the width of this bitmap divided
* by the density scale factor.
*
+ * Returns the bitmap's width multiplied by the ratio of the target density to the bitmap's
+ * source density
+ *
* @param targetDensity The density of the target canvas of the bitmap.
* @return The scaled width of this bitmap, according to the density scale factor.
*/
@@ -1520,6 +1523,9 @@
* Convenience method that returns the height of this bitmap divided
* by the density scale factor.
*
+ * Returns the bitmap's height multiplied by the ratio of the target density to the bitmap's
+ * source density
+ *
* @param targetDensity The density of the target canvas of the bitmap.
* @return The scaled height of this bitmap, according to the density scale factor.
*/
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 7ec76d7..99d8c1b 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -20,7 +20,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.os.IBinder;
@@ -28,13 +27,16 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.TimeUtils;
import android.view.FrameMetricsObserver;
import android.view.IGraphicsStats;
import android.view.IGraphicsStatsCallback;
import android.view.NativeVectorDrawableAnimator;
+import android.view.PixelCopy;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.TextureLayer;
+import android.view.animation.AnimationUtils;
import com.android.internal.util.VirtualRefBasePtr;
@@ -42,6 +44,7 @@
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
import sun.misc.Cleaner;
@@ -50,13 +53,8 @@
* from {@link RenderNode}'s to an output {@link android.view.Surface}. There can be as many
* HardwareRenderer instances as desired.</p>
*
- * <h3>Threading</h3>
- * <p>HardwareRenderer is not thread safe. An instance of a HardwareRenderer must only be
- * created & used from a single thread. It does not matter what thread is used, however
- * it must have a {@link android.os.Looper}. Multiple instances do not have to share the same
- * thread, although they can.</p>
- *
* <h3>Resources & lifecycle</h3>
+ *
* <p>All HardwareRenderer instances share a common render thread. The render thread contains
* the GPU context & resources necessary to do GPU-accelerated rendering. As such, the first
* HardwareRenderer created comes with the cost of also creating the associated GPU contexts,
@@ -64,6 +62,7 @@
* is to have a HardwareRenderer instance for every active {@link Surface}. For example
* when an Activity shows a Dialog the system internally will use 2 hardware renderers, both
* of which may be drawing at the same time.</p>
+ *
* <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that
* any {@link Surface} used must have a prompt, reliable consuming side. System-provided
* consumers such as {@link android.view.SurfaceView},
@@ -73,8 +72,6 @@
* it is the app's responsibility to ensure that they consume updates promptly and rapidly.
* Failure to do so will cause the render thread to stall on that surface, blocking all
* HardwareRenderer instances.</p>
- *
- * @hide
*/
public class HardwareRenderer {
private static final String LOG_TAG = "HardwareRenderer";
@@ -89,18 +86,18 @@
* The renderer is requesting a redraw. This can occur if there's an animation that's running
* in the RenderNode tree and the hardware renderer is unable to self-animate.
*
- * If this is returned from syncAndDrawFrame the expectation is that syncAndDrawFrame
+ * <p>If this is returned from syncAndDraw the expectation is that syncAndDraw
* will be called again on the next vsync signal.
*/
public static final int SYNC_REDRAW_REQUESTED = 1 << 0;
/**
* The hardware renderer no longer has a valid {@link android.view.Surface} to render to.
- * This can happen if {@link Surface#destroy()} was called. The user should no longer
- * attempt to call syncAndDrawFrame until a new surface has been provided by calling
+ * This can happen if {@link Surface#release()} was called. The user should no longer
+ * attempt to call syncAndDraw until a new surface has been provided by calling
* setSurface.
*
- * Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
+ * <p>Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
*/
public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
@@ -119,6 +116,7 @@
*/
public static final int SYNC_FRAME_DROPPED = 1 << 3;
+ /** @hide */
@IntDef(value = {
SYNC_OK, SYNC_REDRAW_REQUESTED, SYNC_LOST_SURFACE_REWARD_IF_FOUND,
SYNC_CONTEXT_IS_STOPPED, SYNC_FRAME_DROPPED})
@@ -153,7 +151,6 @@
protected RenderNode mRootNode;
private boolean mOpaque = true;
private boolean mForceDark = false;
- private FrameInfo mScratchInfo;
private boolean mIsWideGamut = false;
/**
@@ -175,14 +172,14 @@
* Destroys the rendering context of this HardwareRenderer. This destroys the resources
* associated with this renderer and releases the currently set {@link Surface}.
*
- * The renderer may be restored from this state by setting a new {@link Surface}, setting
+ * <p>The renderer may be restored from this state by setting a new {@link Surface}, setting
* new rendering content with {@link #setContentRoot(RenderNode)}, and resuming
- * rendering with {@link #syncAndDrawFrame(long)}.
+ * rendering by issuing a new {@link FrameRenderRequest}.
*
- * It is suggested to call this in response to callbacks such as
+ * <p>It is suggested to call this in response to callbacks such as
* {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}.
*
- * Note that if there are any outstanding frame commit callbacks they may end up never being
+ * <p>Note that if there are any outstanding frame commit callbacks they may never being
* invoked if the frame was deferred to a later vsync.
*/
public void destroy() {
@@ -204,14 +201,14 @@
* Sets the center of the light source. The light source point controls the directionality
* and shape of shadows rendered by RenderNode Z & elevation.
*
- * The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set
+ * <p>The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set
* lightY to 0 - windowTop, lightZ set to 600dp, and lightRadius to 800dp.
*
- * The light source should be setup both as part of initial configuration, and whenever
+ * <p>The light source should be setup both as part of initial configuration, and whenever
* the window moves to ensure the light source stays anchored in display space instead
* of in window space.
*
- * This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
+ * <p>This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
* before shadows will work.
*
* @param lightX The X position of the light source
@@ -233,10 +230,10 @@
* Configures the ambient & spot shadow alphas. This is the alpha used when the shadow
* has max alpha, and ramps down from the values provided to zero.
*
- * These values are typically provided by the current theme, see
+ * <p>These values are typically provided by the current theme, see
* {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}.
*
- * This must be set at least once along with
+ * <p>This must be set at least once along with
* {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work.
*
* @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default
@@ -254,8 +251,8 @@
/**
* Sets the content root to render. It is not necessary to call this whenever the content
* recording changes. Any mutations to the RenderNode content, or any of the RenderNode's
- * contained within the content node, will be applied whenever {@link #syncAndDrawFrame(long)}
- * is called.
+ * contained within the content node, will be applied whenever a new {@link FrameRenderRequest}
+ * is issued via {@link #createRenderRequest()} and {@link FrameRenderRequest#syncAndDraw()}.
*
* @param content The content to set as the root RenderNode. If null the content root is removed
* and the renderer will draw nothing.
@@ -295,6 +292,126 @@
}
/**
+ * Sets the parameters that can be used to control a render request for a
+ * {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer
+ * than a single frame request.
+ */
+ public final class FrameRenderRequest {
+ private FrameInfo mFrameInfo = new FrameInfo();
+ private boolean mWaitForPresent;
+
+ private FrameRenderRequest() { }
+
+ private void reset() {
+ mWaitForPresent = false;
+ // Default to the animation time which, if choreographer is in play, will default to the
+ // current vsync time. Otherwise it will be 'now'.
+ mRenderRequest.setVsyncTime(
+ AnimationUtils.currentAnimationTimeMillis() * TimeUtils.NANOS_PER_MS);
+ }
+
+ /** @hide */
+ public void setFrameInfo(FrameInfo info) {
+ System.arraycopy(info.frameInfo, 0, mFrameInfo.frameInfo, 0, info.frameInfo.length);
+ }
+
+ /**
+ * Sets the vsync time that represents the start point of this frame. Typically this
+ * comes from {@link android.view.Choreographer.FrameCallback}. Other compatible time
+ * sources include {@link System#nanoTime()}, however if the result is being displayed
+ * on-screen then using {@link android.view.Choreographer} is strongly recommended to
+ * ensure smooth animations.
+ *
+ * <p>If the clock source is not from a CLOCK_MONOTONIC source then any animations driven
+ * directly by RenderThread will not be synchronized properly with the current frame.
+ *
+ * @param vsyncTime The vsync timestamp for this frame. The timestamp is in nanoseconds
+ * and should come from a CLOCK_MONOTONIC source.
+ *
+ * @return this instance
+ */
+ public FrameRenderRequest setVsyncTime(long vsyncTime) {
+ mFrameInfo.setVsync(vsyncTime, vsyncTime);
+ mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
+ return this;
+ }
+
+ /**
+ * Adds a frame commit callback. This callback will be invoked when the current rendering
+ * content has been rendered into a frame and submitted to the swap chain. The frame may
+ * not currently be visible on the display when this is invoked, but it has been submitted.
+ * This callback is useful in combination with {@link PixelCopy} to capture the current
+ * rendered content of the UI reliably.
+ *
+ * @param executor The executor to run the callback on. It is strongly recommended that
+ * this executor post to a different thread, as the calling thread is
+ * highly sensitive to being blocked.
+ * @param frameCommitCallback The callback to invoke when the frame content has been drawn.
+ * Will be invoked on the given {@link Executor}.
+ *
+ * @return this instance
+ */
+ public FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
+ @NonNull Runnable frameCommitCallback) {
+ setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
+ return this;
+ }
+
+ /**
+ * Sets whether or not {@link #syncAndDraw()} should block until the frame has been
+ * presented. If this is true and {@link #syncAndDraw()} does not return
+ * {@link #SYNC_FRAME_DROPPED} or an error then when {@link #syncAndDraw()} has returned
+ * the frame has been submitted to the {@link Surface}. The default and typically
+ * recommended value is false, as blocking for present will prevent pipelining from
+ * happening, reducing overall throughput. This is useful for situations such as
+ * {@link SurfaceHolder.Callback2#surfaceRedrawNeeded(SurfaceHolder)} where it is desired
+ * to block until a frame has been presented to ensure first-frame consistency with
+ * other Surfaces.
+ *
+ * @param shouldWait If true the next call to {@link #syncAndDraw()} will block until
+ * completion.
+ * @return this instance
+ */
+ public FrameRenderRequest setWaitForPresent(boolean shouldWait) {
+ mWaitForPresent = shouldWait;
+ return this;
+ }
+
+ /**
+ * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. This
+ * {@link FrameRenderRequest} instance should no longer be used after calling this method.
+ * The system internally may reuse instances of {@link FrameRenderRequest} to reduce
+ * allocation churn.
+ *
+ * @return The result of the sync operation. See {@link SyncAndDrawResult}.
+ */
+ @SyncAndDrawResult
+ public int syncAndDraw() {
+ int syncResult = syncAndDrawFrame(mFrameInfo);
+ if (mWaitForPresent && (syncResult & SYNC_FRAME_DROPPED) == 0) {
+ fence();
+ }
+ return syncResult;
+ }
+ }
+
+ private FrameRenderRequest mRenderRequest = new FrameRenderRequest();
+
+ /**
+ * Returns a {@link FrameRenderRequest} that can be used to render a new frame. This is used
+ * to synchronize the RenderNode content provided by {@link #setContentRoot(RenderNode)} with
+ * the RenderThread and then renders a single frame to the Surface set with
+ * {@link #setSurface(Surface)}.
+ *
+ * @return An instance of {@link FrameRenderRequest}. The instance may be reused for every
+ * frame, so the caller should not hold onto it for longer than a single render request.
+ */
+ public FrameRenderRequest createRenderRequest() {
+ mRenderRequest.reset();
+ return mRenderRequest;
+ }
+
+ /**
* Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
*
* @hide
@@ -305,54 +422,15 @@
}
/**
- * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
- *
- * @param vsyncTime The vsync timestamp for this frame. Typically this comes from
- * {@link android.view.Choreographer.FrameCallback}. Must be set and be valid
- * as the renderer uses this time internally to drive animations.
- * @return The result of the sync operation. See {@link SyncAndDrawResult}.
- */
- @SyncAndDrawResult
- public int syncAndDrawFrame(long vsyncTime) {
- if (mScratchInfo == null) {
- mScratchInfo = new FrameInfo();
- }
- mScratchInfo.setVsync(vsyncTime, vsyncTime);
- mScratchInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
- return syncAndDrawFrame(mScratchInfo);
- }
-
- /**
- * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
- * frameCommitCallback callback will be invoked when the current rendering content has been
- * rendered into a frame and submitted to the swap chain.
- *
- * @param vsyncTime The vsync timestamp for this frame. Typically this comes from
- * {@link android.view.Choreographer.FrameCallback}. Must be set and
- * be valid as the renderer uses this time internally to drive
- * animations.
- * @param frameCommitCallback The callback to invoke when the frame content has been drawn.
- * Will be invoked on the current {@link android.os.Looper} thread.
- * @return The result of the sync operation. See {@link SyncAndDrawResult}.
- */
- @SyncAndDrawResult
- public int syncAndDrawFrame(long vsyncTime,
- @Nullable Runnable frameCommitCallback) {
- if (frameCommitCallback != null) {
- setFrameCompleteCallback(frameNr -> frameCommitCallback.run());
- }
- return syncAndDrawFrame(vsyncTime);
- }
-
- /**
* Suspends any current rendering into the surface but do not do any destruction. This
* is useful to temporarily suspend using the active Surface in order to do any Surface
* mutations necessary.
*
- * Any subsequent draws will override the pause, resuming normal operation.
+ * <p>Any subsequent draws will override the pause, resuming normal operation.
*
* @return true if there was an outstanding render request, false otherwise. If this is true
- * the caller should ensure that {@link #syncAndDrawFrame(long)} is called at the soonest
+ * the caller should ensure that {@link #createRenderRequest()}
+ * and {@link FrameRenderRequest#syncAndDraw()} is called at the soonest
* possible time to resume normal operation.
*
* TODO Should this be exposed? ViewRootImpl needs it because it destroys the old
@@ -367,14 +445,14 @@
/**
* Hard stops rendering into the surface. If the renderer is stopped it will
- * block any attempt to render. Calls to {@link #syncAndDrawFrame(long)} will still
- * sync over the latest rendering content, however they will not render and instead
+ * block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will
+ * still sync over the latest rendering content, however they will not render and instead
* {@link #SYNC_CONTEXT_IS_STOPPED} will be returned.
*
- * If false is passed then rendering will resume as normal. Any pending rendering requests
+ * <p>If false is passed then rendering will resume as normal. Any pending rendering requests
* will produce a new frame at the next vsync signal.
*
- * This is useful in combination with lifecycle events such as {@link Activity#onStop()}
+ * <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()}
* and {@link Activity#onStart()}.
*
* @param stopped true to stop all rendering, false to resume
@@ -384,24 +462,26 @@
}
/**
- * Destroys all hardware rendering resources associated with the current rendering content.
+ * Destroys all the display lists associated with the current rendering content.
* This includes releasing a reference to the current content root RenderNode. It will
* therefore be necessary to call {@link #setContentRoot(RenderNode)} in order to resume
- * rendering after calling this.
+ * rendering after calling this, along with re-recording the display lists for the
+ * RenderNode tree.
*
- * It is recommended, but not necessary, to use this in combination with lifecycle events
+ * <p>It is recommended, but not necessary, to use this in combination with lifecycle events
* such as {@link Activity#onStop()} and {@link Activity#onStart()} or in response to
* {@link android.content.ComponentCallbacks2#onTrimMemory(int)} signals such as
* {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN}
*
* See also {@link #setStopped(boolean)}
*/
- public void destroyHardwareResources() {
+ public void clearContent() {
nDestroyHardwareResources(mNativeProxy);
}
/**
* Whether or not the force-dark feature should be used for this renderer.
+ * @hide
*/
public boolean setForceDark(boolean enable) {
if (mForceDark != enable) {
@@ -415,20 +495,24 @@
/**
* Allocate buffers ahead of time to avoid allocation delays during rendering.
*
- * Typically a Surface will allocate buffers lazily. This is usually fine and reduces the
+ * <p>Typically a Surface will allocate buffers lazily. This is usually fine and reduces the
* memory usage of Surfaces that render rarely or never hit triple buffering. However
* for UI it can result in a slight bit of jank on first launch. This hint will
* tell the HardwareRenderer that now is a good time to allocate the 3 buffers
* necessary for typical rendering.
*
- * Must be called after a {@link Surface} has been set.
+ * <p>Must be called after a {@link Surface} has been set.
+ *
+ * TODO: Figure out if we even need/want this. Should HWUI just be doing this in response
+ * to setSurface anyway? Vulkan swapchain makes this murky, so delay making it public
+ * @hide
*/
public void allocateBuffers() {
nAllocateBuffers(mNativeProxy);
}
/**
- * Notifies the hardware renderer that a call to {@link #syncAndDrawFrame(long)} will
+ * Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} will
* be coming soon. This is used to help schedule when RenderThread-driven animations will
* happen as the renderer wants to avoid producing more than one frame per vsync signal.
*/
@@ -439,7 +523,7 @@
/**
* Change the HardwareRenderer's opacity. Will take effect on the next frame produced.
*
- * If the renderer is set to opaque it is the app's responsibility to ensure that the
+ * <p>If the renderer is set to opaque it is the app's responsibility to ensure that the
* content renders to every pixel of the Surface, otherwise corruption may result. Note that
* this includes ensuring that the first draw of any given pixel does not attempt to blend
* against the destination. If this is false then the hardware renderer will clear to
@@ -527,7 +611,7 @@
}
/**
- * Prevents any further drawing until {@link #syncAndDrawFrame(long)} is called.
+ * Prevents any further drawing until {@link FrameRenderRequest#syncAndDraw()} is called.
* This is a signal that the contents of the RenderNode tree are no longer safe to play back.
* In practice this usually means that there are Functor pointers in the
* display list that are no longer valid.
@@ -718,10 +802,8 @@
* Interface for listening to picture captures
* @hide
*/
- @TestApi
public interface PictureCapturedCallback {
/** @hide */
- @TestApi
void onPictureCaptured(Picture picture);
}
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 26c5080..9b5e330 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -372,7 +372,7 @@
}
mResources = res;
mInputStream = is;
- mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
+ mInputDensity = inputDensity;
}
final Resources mResources;
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 789e38c..d7aee77 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -43,6 +43,7 @@
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.os.Build;
+import android.os.Handler;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.IntArray;
@@ -1241,6 +1242,7 @@
// If the duration of an animation is more than 300 frames, we cap the sample size to 300.
private static final int MAX_SAMPLE_POINTS = 300;
+ private Handler mHandler;
private AnimatorListener mListener = null;
private final LongArray mStartDelays = new LongArray();
private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -1671,6 +1673,9 @@
.mRootName);
}
mStarted = true;
+ if (mHandler == null) {
+ mHandler = new Handler();
+ }
nStart(mSetPtr, this, ++mLastListenerId);
invalidateOwningView();
if (mListener != null) {
@@ -1780,7 +1785,7 @@
// onFinished: should be called from native
@UnsupportedAppUsage
private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
- set.onAnimationEnd(id);
+ set.mHandler.post(() -> set.onAnimationEnd(id));
}
private void transferPendingActions(VectorDrawableAnimator animatorSet) {
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index 8cdcc46..0289d3f 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -68,10 +68,12 @@
void processQueue() { mQueue.process(); }
virtual bool threadLoop() override {
+ Looper::setForThread(mLooper);
while (!exitPending()) {
waitForWork();
processQueue();
}
+ Looper::setForThread(nullptr);
return false;
}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index c6a207f..90db207 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -247,6 +247,12 @@
}
@Override
+ public void notifyCaptivePortalAppFinished(int response) {
+ checkNetworkStackCallingPermission();
+ mNm.notifyCaptivePortalAppFinished(response);
+ }
+
+ @Override
public void forceReevaluation(int uid) {
checkNetworkStackCallingPermission();
mNm.forceReevaluation(uid);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index b9e901b..ec4a479 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -39,9 +39,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.CaptivePortal;
import android.net.ConnectivityManager;
-import android.net.ICaptivePortal;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.LinkProperties;
@@ -460,6 +458,13 @@
sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
}
+ /**
+ * Notify that the captive portal app was closed with the provided response code.
+ */
+ public void notifyCaptivePortalAppFinished(int response) {
+ sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
+ }
+
@Override
protected void log(String s) {
if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s);
@@ -671,29 +676,8 @@
case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
final Bundle appExtras = new Bundle();
// OneAddressPerFamilyNetwork is not parcelable across processes.
- appExtras.putParcelable(
- ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
- appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
- new CaptivePortal(new ICaptivePortal.Stub() {
- @Override
- public void appResponse(int response) {
- if (response == APP_RETURN_WANTED_AS_IS) {
- mContext.enforceCallingPermission(
- PERMISSION_NETWORK_SETTINGS,
- "CaptivePortal");
- }
- sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
- }
-
- @Override
- public void logEvent(int eventId, String packageName)
- throws RemoteException {
- mContext.enforceCallingPermission(
- PERMISSION_NETWORK_SETTINGS,
- "CaptivePortal");
- mCallback.logCaptivePortalLoginEvent(eventId, packageName);
- }
- }));
+ final Network network = new Network(mNetwork);
+ appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
if (probeRes.probeSpec != null) {
@@ -702,7 +686,7 @@
}
appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
mCaptivePortalUserAgent);
- mCm.startCaptivePortalApp(appExtras);
+ mCm.startCaptivePortalApp(network, appExtras);
return HANDLED;
default:
return NOT_HANDLED;
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index 4a09b3e..5c7b514 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -18,6 +18,7 @@
name: "NetworkStackTests",
certificate: "platform",
srcs: ["src/**/*.java"],
+ test_suites: ["device-tests"],
resource_dirs: ["res"],
static_libs: [
"android-support-test",
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index a4a1000..af71ac5 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -22,6 +22,7 @@
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_STREAM;
@@ -1017,6 +1018,7 @@
private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
+ private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
private static final byte[] IPV4_BROADCAST_ADDRESS =
{(byte) 255, (byte) 255, (byte) 255, (byte) 255};
@@ -1568,7 +1570,7 @@
// Verify IPv4 packet from another address is passed
assertPass(program,
ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
- anotherDstPort, anotherSeqNum, anotherAckNum));
+ anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
// Remove IPv4 keepalive filter
apfFilter.removeKeepalivePacketFilter(slot1);
@@ -1613,15 +1615,15 @@
// dst: 10.0.0.5, port: 12345
assertDrop(program,
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1));
+ dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
// Verify IPv4 non-keepalive ack packet from the same source address is passed
assertPass(program,
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum + 100, seqNum));
+ dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
// Verify IPv4 packet from another address is passed
assertPass(program,
ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
- anotherDstPort, anotherSeqNum, anotherAckNum));
+ anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
// Verify IPv6 keepalive ack packet is dropped
// src: 2404:0:0:0:0:0:faf2, port: 54321
@@ -1650,13 +1652,13 @@
// Verify IPv4, IPv6 packets are passed
assertPass(program,
ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1));
+ dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
assertPass(program,
ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
dstPort, srcPort, ackNum, seqNum + 1));
assertPass(program,
ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
- dstPort, anotherSeqNum, anotherAckNum));
+ dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
assertPass(program,
ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
dstPort, anotherSeqNum, anotherAckNum));
@@ -1664,28 +1666,30 @@
apfFilter.shutdown();
}
- private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
- int dport, int seq, int ack) {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport,
+ int dport, int seq, int ack, int dataLength) {
+ final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
+
+ ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
+
+ // ether type
packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+
+ // IPv4 header
packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+ packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+ packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP);
put(packet, IPV4_SRC_ADDR_OFFSET, sip);
- put(packet, IPV4_DEST_ADDR_OFFSET, tip);
+ put(packet, IPV4_DEST_ADDR_OFFSET, dip);
packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
- return packet.array();
- }
- private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
- int dport, int seq, int ack, int dataLength) {
- final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
-
- ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack));
- packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
- // TCP header length 5, reserved 3 bits, NS=0
+ // TCP header length 5(20 bytes), reserved 3 bits, NS=0
packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
+ // TCP flags: ACK set
+ packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10);
return packet.array();
}
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index b98b0f7..9a16bb7 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,8 +16,7 @@
package com.android.server.connectivity;
-import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL;
+import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -41,8 +40,6 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.Intent;
-import android.net.CaptivePortal;
import android.net.ConnectivityManager;
import android.net.INetworkMonitorCallbacks;
import android.net.InetAddresses;
@@ -54,10 +51,10 @@
import android.net.metrics.IpConnectivityLog;
import android.net.util.SharedLog;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -487,19 +484,23 @@
// Check that startCaptivePortalApp sends the expected intent.
nm.launchCaptivePortalApp();
- final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1))
- .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT));
- final Intent intent = intentCaptor.getValue();
- assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
- final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
- assertEquals(TEST_NETID, network.netId);
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+ verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
+ final Bundle bundle = bundleCaptor.getValue();
+ final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
+ assertEquals(TEST_NETID, bundleNetwork.netId);
+ // network is passed both in bundle and as parameter, as the bundle is opaque to the
+ // framework and only intended for the captive portal app, but the framework needs
+ // the network to identify the right NetworkMonitor.
+ assertEquals(TEST_NETID, networkCaptor.getValue().netId);
// Have the app report that the captive portal is dismissed, and check that we revalidate.
setStatus(mHttpsConnection, 204);
setStatus(mHttpConnection, 204);
- final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL);
- captivePortal.reportCaptivePortalDismissed();
+
+ nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
.notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3778a9c..4a5388b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -126,6 +126,7 @@
<uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
+ <uses-permission android:name="android.permission.MANAGE_BIOMETRIC" />
<uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
<uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
diff --git a/packages/SystemUI/res/drawable/rounded_bg.xml b/packages/SystemUI/res/drawable/rounded_bg.xml
index c23a87f..3de67de 100644
--- a/packages/SystemUI/res/drawable/rounded_bg.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg.xml
@@ -3,8 +3,8 @@
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimary" />
<corners
- android:bottomLeftRadius="@dimen/corner_size"
- android:topLeftRadius="@dimen/corner_size"
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
android:bottomRightRadius="0dp"
android:topRightRadius="0dp"
/>
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
index b3bea63..7db59e9 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
@@ -3,7 +3,7 @@
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimaryDark" />
<corners
- android:bottomLeftRadius="@dimen/corner_size"
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
android:topLeftRadius="0dp"
android:bottomRightRadius="0dp"
android:topRightRadius="0dp"
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
index 622226f..382ca20 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
@@ -3,9 +3,9 @@
android:shape="rectangle">
<solid android:color="?android:attr/panelColorBackground" />
<corners
- android:bottomLeftRadius="@dimen/corner_size"
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
android:topLeftRadius="0dp"
- android:bottomRightRadius="@dimen/corner_size"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="0dp"
/>
</shape>
diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml
index 03f18bb..e0d3f63 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_full.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml
@@ -3,9 +3,9 @@
android:shape="rectangle">
<solid android:color="?android:attr/colorBackgroundFloating" />
<corners
- android:bottomLeftRadius="@dimen/corner_size"
- android:topLeftRadius="@dimen/corner_size"
- android:bottomRightRadius="@dimen/corner_size"
- android:topRightRadius="@dimen/corner_size"
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius"
/>
</shape>
diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
index a4b3c99..a62657d 100644
--- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
@@ -17,9 +17,9 @@
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimaryDark" />
<corners
- android:bottomLeftRadius="@dimen/corner_size"
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
android:topLeftRadius="0dp"
- android:bottomRightRadius="@dimen/corner_size"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="0dp"
/>
</shape>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b4131d7..01595f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1582,19 +1582,19 @@
<!-- Notification Inline controls: continue receiving notifications prompt, channel level -->
<string name="inline_keep_showing">Keep showing these notifications?</string>
- <!-- Notification inline controls: block notifications button -->
+ <!-- Notification inline controls: block notifications button [CHAR_LIMIT=25] -->
<string name="inline_stop_button">Stop notifications</string>
<!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=35] -->
<string name="inline_deliver_silently_button">Deliver Silently</string>
- <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
+ <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=20] -->
<string name="inline_block_button">Block</string>
- <!-- Notification inline controls: keep getting notifications button -->
+ <!-- Notification inline controls: keep getting notifications button [CHAR_LIMIT=25] -->
<string name="inline_keep_button">Keep showing</string>
- <!-- Notification inline controls: minimize notifications button -->
+ <!-- Notification inline controls: minimize notifications button [CHAR_LIMIT=20] -->
<string name="inline_minimize_button">Minimize</string>
<!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 038f491..83398cf 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -122,23 +122,9 @@
}
@Override
- public void onTranscriptionUpdate(String transcription) {
+ public void onSetUiHints(Bundle hints) {
if (VERBOSE) {
- Log.v(TAG, "Transcription Updated: \"" + transcription + "\"");
- }
- }
-
- @Override
- public void onTranscriptionComplete(boolean immediate) {
- if (VERBOSE) {
- Log.v(TAG, "Transcription complete (immediate=" + immediate + ")");
- }
- }
-
- @Override
- public void onVoiceStateChange(int state) {
- if (VERBOSE) {
- Log.v(TAG, "Voice state is now " + state);
+ Log.v(TAG, "UI hints received");
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index a67e1b7..e62c77d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -52,7 +52,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import java.util.List;
@@ -150,7 +150,8 @@
}
@Inject
- public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
+ public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
+ BubbleData data) {
mContext = context;
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
@@ -171,7 +172,7 @@
mTaskStackListener = new BubbleTaskStackListener();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
- mBubbleData = BubbleData.getInstance();
+ mBubbleData = data;
}
/**
@@ -253,7 +254,7 @@
mStackView.updateBubble(notif, updatePosition);
} else {
if (mStackView == null) {
- mStackView = new BubbleStackView(mContext);
+ mStackView = new BubbleStackView(mContext, mBubbleData);
ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
// XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim
// between bubble and the shade
@@ -314,8 +315,7 @@
}
@Override
- public void onEntryInflated(NotificationEntry entry,
- @NotificationInflater.InflationFlag int inflatedFlags) {
+ public void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
if (!areBubblesEnabled(mContext)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 89b0de8..5002f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -22,23 +22,19 @@
import java.util.Collection;
import java.util.HashMap;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Keeps track of active bubbles.
*/
+@Singleton
class BubbleData {
private HashMap<String, Bubble> mBubbles = new HashMap<>();
- private static BubbleData sBubbleData = null;
-
- private BubbleData() {}
-
- public static BubbleData getInstance() {
- if (sBubbleData == null) {
- sBubbleData = new BubbleData();
- }
- return sBubbleData;
- }
+ @Inject
+ BubbleData() {}
/**
* The set of bubbles.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 3389c46..492eadd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -107,8 +107,10 @@
private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
@Override
public void onActivityViewReady(ActivityView view) {
- mActivityViewReady = true;
- mActivityView.startActivity(mBubbleIntent);
+ if (!mActivityViewReady) {
+ mActivityViewReady = true;
+ mActivityView.startActivity(mBubbleIntent);
+ }
}
@Override
@@ -262,6 +264,12 @@
updateHeaderView();
updatePermissionView();
updateExpandedView();
+ }
+
+ /**
+ * Lets activity view know it should be shown / populated.
+ */
+ public void populateActivityView() {
mActivityView.setCallback(mStateCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 8bc83d4..ed9b38b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -58,7 +58,7 @@
/**
* Renders bubbles in a stack and handles animating expanded and collapsed states.
*/
-public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleStackView extends FrameLayout {
private static final String TAG = "BubbleStackView";
/**
@@ -85,6 +85,7 @@
private final SpringAnimation mExpandedViewXAnim;
private final SpringAnimation mExpandedViewYAnim;
+ private final BubbleData mBubbleData;
private PhysicsAnimationLayout mBubbleContainer;
private StackAnimationController mStackAnimationController;
@@ -92,12 +93,12 @@
private FrameLayout mExpandedViewContainer;
- private BubbleData mBubbleData;
private int mBubbleSize;
private int mBubblePadding;
private int mExpandedAnimateXDistance;
private int mExpandedAnimateYDistance;
+ private int mStatusBarHeight;
private Bubble mExpandedBubble;
private boolean mIsExpanded;
@@ -140,13 +141,14 @@
}
};
- public BubbleStackView(Context context) {
+ public BubbleStackView(Context context, BubbleData data) {
super(context);
- mBubbleData = BubbleData.getInstance();
+ mBubbleData = data;
mInflater = LayoutInflater.from(context);
- mTouchHandler = new BubbleTouchHandler(context);
+ mTouchHandler = new BubbleTouchHandler(context, this);
setOnTouchListener(mTouchHandler);
+ mInflater = LayoutInflater.from(context);
Resources res = getResources();
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -155,6 +157,8 @@
res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
mExpandedAnimateYDistance =
res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance);
+ mStatusBarHeight =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mDisplaySize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -196,6 +200,8 @@
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
setClipChildren(false);
+
+ mBubbleContainer.bringToFront();
}
@Override
@@ -481,9 +487,10 @@
if (shouldExpand) {
mBubbleContainer.setController(mExpandedAnimationController);
mExpandedAnimationController.expandFromStack(
- mStackAnimationController.getStackPosition(), () -> {
- updatePointerPosition();
- updateAfter.run();
+ mStackAnimationController.getStackPosition(),
+ () -> {
+ updatePointerPosition();
+ updateAfter.run();
});
} else {
mBubbleContainer.cancelAllAnimations();
@@ -541,55 +548,49 @@
: null;
}
- @Override
- public void setPosition(float x, float y) {
- mStackAnimationController.moveFirstBubbleWithStackFollowing(x, y);
- }
-
- @Override
- public void setPositionX(float x) {
- // Unsupported, use setPosition(x, y).
- }
-
- @Override
- public void setPositionY(float y) {
- // Unsupported, use setPosition(x, y).
- }
-
- @Override
- public PointF getPosition() {
+ public PointF getStackPosition() {
return mStackAnimationController.getStackPosition();
}
/** Called when a drag operation on an individual bubble has started. */
- public void onBubbleDragStart(BubbleView bubble) {
- // TODO: Save position and snap back if not dismissed.
+ public void onBubbleDragStart(View bubble) {
+ mExpandedAnimationController.prepareForBubbleDrag(bubble);
}
/** Called with the coordinates to which an individual bubble has been dragged. */
- public void onBubbleDragged(BubbleView bubble, float x, float y) {
- bubble.setTranslationX(x);
- bubble.setTranslationY(y);
+ public void onBubbleDragged(View bubble, float x, float y) {
+ if (!mIsExpanded || mIsAnimating) {
+ return;
+ }
+
+ mExpandedAnimationController.dragBubbleOut(bubble, x, y);
}
/** Called when a drag operation on an individual bubble has finished. */
- public void onBubbleDragFinish(BubbleView bubble, float x, float y, float velX, float velY) {
- // TODO: Add fling to bottom to dismiss.
+ public void onBubbleDragFinish(
+ View bubble, float x, float y, float velX, float velY, boolean dismissed) {
+ if (!mIsExpanded || mIsAnimating) {
+ return;
+ }
+
+ if (dismissed) {
+ mExpandedAnimationController.prepareForDismissalWithVelocity(bubble, velX, velY);
+ } else {
+ mExpandedAnimationController.snapBubbleBack(bubble, velX, velY);
+ }
}
void onDragStart() {
- if (mIsExpanded) {
+ if (mIsExpanded || mIsAnimating) {
return;
}
mStackAnimationController.cancelStackPositionAnimations();
mBubbleContainer.setController(mStackAnimationController);
- mIsAnimating = false;
}
void onDragged(float x, float y) {
- // TODO: We can drag if animating - just need to reroute inflight anims to drag point.
- if (mIsExpanded) {
+ if (mIsExpanded || mIsAnimating) {
return;
}
@@ -678,7 +679,7 @@
if (getRootWindowInsets() != null) {
WindowInsets insets = getRootWindowInsets();
return Math.max(
- insets.getSystemWindowInsetTop(),
+ mStatusBarHeight,
insets.getDisplayCutout() != null
? insets.getDisplayCutout().getSafeInsetTop()
: 0);
@@ -707,6 +708,7 @@
mExpandedViewContainer.removeAllViews();
if (mExpandedBubble != null && mIsExpanded) {
mExpandedViewContainer.addView(mExpandedBubble.expandedView);
+ mExpandedBubble.expandedView.populateActivityView();
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
}
}
@@ -740,9 +742,9 @@
private void updatePointerPosition() {
if (mExpandedBubble != null) {
- float pointerPosition = mExpandedBubble.iconView.getPosition().x
+ float pointerPosition = mExpandedBubble.iconView.getTranslationX()
+ (mExpandedBubble.iconView.getWidth() / 2f);
- mExpandedBubble.expandedView.setPointerPosition(pointerPosition);
+ mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition);
}
}
@@ -768,7 +770,7 @@
* @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
*/
public float getNormalizedXPosition() {
- return new BigDecimal(getPosition().x / mDisplaySize.x)
+ return new BigDecimal(getStackPosition().x / mDisplaySize.x)
.setScale(4, RoundingMode.CEILING.HALF_UP)
.floatValue();
}
@@ -777,7 +779,7 @@
* @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
*/
public float getNormalizedYPosition() {
- return new BigDecimal(getPosition().y / mDisplaySize.y)
+ return new BigDecimal(getStackPosition().y / mDisplaySize.y)
.setScale(4, RoundingMode.CEILING.HALF_UP)
.floatValue();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 7d3c0f8..165eb1d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -34,17 +34,16 @@
* dismissing, and flings.
*/
class BubbleTouchHandler implements View.OnTouchListener {
+ /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */
+ private static final float DISMISS_MIN_VELOCITY = 4000f;
+
+ private final PointF mTouchDown = new PointF();
+ private final PointF mViewPositionOnTouchDown = new PointF();
+ private final BubbleStackView mStack;
private BubbleController mController = Dependency.get(BubbleController.class);
private PipDismissViewController mDismissViewController;
- // The position of the bubble on down event
- private float mBubbleDownPosX;
- private float mBubbleDownPosY;
- // The touch position on down event
- private float mDownX = -1;
- private float mDownY = -1;
-
private boolean mMovedEnough;
private int mTouchSlopSquared;
private VelocityTracker mVelocityTracker;
@@ -58,65 +57,42 @@
}
};
- // Bubble being dragged from the row of bubbles when the stack is expanded
- private BubbleView mBubbleDraggingOut;
+ /** View that was initially touched, when we received the first ACTION_DOWN event. */
+ private View mTouchedView;
- /**
- * Views movable by this touch handler should implement this interface.
- */
- public interface FloatingView {
-
- /**
- * Sets the position of the view.
- */
- void setPosition(float x, float y);
-
- /**
- * Sets the x position of the view.
- */
- void setPositionX(float x);
-
- /**
- * Sets the y position of the view.
- */
- void setPositionY(float y);
-
- /**
- * @return the position of the view.
- */
- PointF getPosition();
- }
-
- public BubbleTouchHandler(Context context) {
+ BubbleTouchHandler(Context context, BubbleStackView stackView) {
final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mTouchSlopSquared = touchSlop * touchSlop;
mDismissViewController = new PipDismissViewController(context);
+ mStack = stackView;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
- int action = event.getActionMasked();
+ final int action = event.getActionMasked();
- BubbleStackView stack = (BubbleStackView) v;
- View targetView = mBubbleDraggingOut != null
- ? mBubbleDraggingOut
- : stack.getTargetView(event);
- boolean isFloating = targetView instanceof FloatingView;
- if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) {
- stack.collapseStack();
+ // If we aren't currently in the process of touching a view, figure out what we're touching.
+ // It'll be the stack, an individual bubble, or nothing.
+ if (mTouchedView == null) {
+ mTouchedView = mStack.getTargetView(event);
+ }
+
+ // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
+ // anything, collapse the stack.
+ if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
+ mStack.collapseStack();
cleanUpDismissTarget();
- resetTouches();
+ mTouchedView = null;
return false;
}
- FloatingView floatingView = (FloatingView) targetView;
- boolean isBubbleStack = floatingView instanceof BubbleStackView;
+ final boolean isStack = mStack.equals(mTouchedView);
+ final float rawX = event.getRawX();
+ final float rawY = event.getRawY();
- PointF startPos = floatingView.getPosition();
- float rawX = event.getRawX();
- float rawY = event.getRawY();
- float x = mBubbleDownPosX + rawX - mDownX;
- float y = mBubbleDownPosY + rawY - mDownY;
+ // The coordinates of the touch event, in terms of the touched view's position.
+ final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x;
+ final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y;
switch (action) {
case MotionEvent.ACTION_DOWN:
trackMovement(event);
@@ -124,87 +100,83 @@
mDismissViewController.createDismissTarget();
mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY);
- mBubbleDownPosX = startPos.x;
- mBubbleDownPosY = startPos.y;
- mDownX = rawX;
- mDownY = rawY;
- mMovedEnough = false;
+ mTouchDown.set(rawX, rawY);
- if (isBubbleStack) {
- stack.onDragStart();
+ if (isStack) {
+ mViewPositionOnTouchDown.set(mStack.getStackPosition());
+ mStack.onDragStart();
} else {
- stack.onBubbleDragStart((BubbleView) floatingView);
+ mViewPositionOnTouchDown.set(
+ mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
+ mStack.onBubbleDragStart(mTouchedView);
}
break;
-
case MotionEvent.ACTION_MOVE:
trackMovement(event);
+ final float deltaX = rawX - mTouchDown.x;
+ final float deltaY = rawY - mTouchDown.y;
- if (mBubbleDownPosX == -1 || mDownX == -1) {
- mBubbleDownPosX = startPos.x;
- mBubbleDownPosY = startPos.y;
- mDownX = rawX;
- mDownY = rawY;
- }
- final float deltaX = rawX - mDownX;
- final float deltaY = rawY - mDownY;
if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) {
mMovedEnough = true;
}
if (mMovedEnough) {
- if (floatingView instanceof BubbleView) {
- mBubbleDraggingOut = ((BubbleView) floatingView);
- stack.onBubbleDragged(mBubbleDraggingOut, x, y);
+ if (isStack) {
+ mStack.onDragged(viewX, viewY);
} else {
- stack.onDragged(x, y);
+ mStack.onBubbleDragged(mTouchedView, viewX, viewY);
}
}
+
// TODO - when we're in the target stick to it / animate in some way?
mInDismissTarget = mDismissViewController.updateTarget(
- isBubbleStack ? stack.getBubbleAt(0) : (View) floatingView);
+ isStack ? mStack.getBubbleAt(0) : mTouchedView);
break;
case MotionEvent.ACTION_CANCEL:
- resetTouches();
+ mTouchedView = null;
cleanUpDismissTarget();
break;
case MotionEvent.ACTION_UP:
trackMovement(event);
- if (mInDismissTarget) {
- if (isBubbleStack) {
- mController.dismissStack();
- } else {
- mController.removeBubble(((BubbleView) floatingView).getKey());
- }
+ if (mInDismissTarget && isStack) {
+ mController.dismissStack();
} else if (mMovedEnough) {
mVelocityTracker.computeCurrentVelocity(1000);
final float velX = mVelocityTracker.getXVelocity();
final float velY = mVelocityTracker.getYVelocity();
- if (isBubbleStack) {
- stack.onDragFinish(x, y, velX, velY);
+ if (isStack) {
+ mStack.onDragFinish(viewX, viewY, velX, velY);
} else {
- stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY);
+ final boolean dismissed = mInDismissTarget || velY > DISMISS_MIN_VELOCITY;
+ mStack.onBubbleDragFinish(
+ mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed);
+ if (dismissed) {
+ mController.removeBubble(((BubbleView) mTouchedView).getKey());
+ }
}
- } else if (floatingView.equals(stack.getExpandedBubbleView())) {
- stack.collapseStack();
- } else if (isBubbleStack) {
- if (stack.isExpanded()) {
- stack.collapseStack();
+ } else if (mTouchedView.equals(mStack.getExpandedBubbleView())) {
+ mStack.collapseStack();
+ } else if (isStack) {
+ if (mStack.isExpanded()) {
+ mStack.collapseStack();
} else {
- stack.expandStack();
+ mStack.expandStack();
}
} else {
- stack.setExpandedBubble(((BubbleView) floatingView).getKey());
+ mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey());
}
+
cleanUpDismissTarget();
mVelocityTracker.recycle();
mVelocityTracker = null;
- resetTouches();
+ mTouchedView = null;
+ mMovedEnough = false;
break;
}
+
return true;
}
@@ -216,16 +188,6 @@
mDismissViewController.destroyDismissTarget();
}
- /**
- * Resets anything we care about after a gesture is complete.
- */
- private void resetTouches() {
- mDownX = -1;
- mDownY = -1;
- mBubbleDownPosX = -1;
- mBubbleDownPosY = -1;
- mBubbleDraggingOut = null;
- }
private void trackMovement(MotionEvent event) {
if (mVelocityTracker == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 1a4b1994..b409a31 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -20,7 +20,6 @@
import android.app.Notification;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.PointF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -39,7 +38,7 @@
/**
* A floating object on the screen that can post message updates.
*/
-public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleView extends FrameLayout {
private static final String TAG = "BubbleView";
// Same value as Launcher3 badge code
@@ -217,25 +216,4 @@
// XXX: should we pull from the drawable, app icon, notif tint?
return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA);
}
-
- @Override
- public void setPosition(float x, float y) {
- setPositionX(x);
- setPositionY(y);
- }
-
- @Override
- public void setPositionX(float x) {
- setTranslationX(x);
- }
-
- @Override
- public void setPositionY(float y) {
- setTranslationY(y);
- }
-
- @Override
- public PointF getPosition() {
- return new PointF(getTranslationX(), getTranslationY());
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 1644064..9fd26b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles.animation;
+import android.content.res.Resources;
import android.graphics.PointF;
import android.view.View;
import android.view.WindowInsets;
@@ -43,6 +44,9 @@
*/
private static final int ANIMATE_TRANSLATION_FACTOR = 4;
+ /** How much to scale down bubbles when they're animating in/out. */
+ private static final float ANIMATE_SCALE_PERCENT = 0.5f;
+
/**
* The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack}
* and used to return to stack form in {@link #collapseBackToStack}.
@@ -55,16 +59,35 @@
private float mBubblePaddingPx;
/** Size of each bubble. */
private float mBubbleSizePx;
+ /** Height of the status bar. */
+ private float mStatusBarHeight;
+
+ /**
+ * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
+ * the rest of the bubbles to animate to fill the gap.
+ */
+ private boolean mBubbleDraggedOutEnough = false;
+
+ /** The bubble currently being dragged out of the row (to potentially be dismissed). */
+ private View mBubbleDraggingOut;
+
+ /**
+ * Drag velocities for the dragging-out bubble when the drag finished. These are used by
+ * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity.
+ */
+ private float mBubbleDraggingOutVelX;
+ private float mBubbleDraggingOutVelY;
@Override
protected void setLayout(PhysicsAnimationLayout layout) {
super.setLayout(layout);
- mStackOffsetPx = layout.getResources().getDimensionPixelSize(
- R.dimen.bubble_stack_offset);
- mBubblePaddingPx = layout.getResources().getDimensionPixelSize(
- R.dimen.bubble_padding);
- mBubbleSizePx = layout.getResources().getDimensionPixelSize(
- R.dimen.individual_bubble_size);
+
+ final Resources res = layout.getResources();
+ mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
+ mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding);
+ mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+ mStatusBarHeight =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
/**
@@ -98,12 +121,93 @@
runAfterTranslationsEnd(after);
}
+ /** Prepares the given bubble to be dragged out. */
+ public void prepareForBubbleDrag(View bubble) {
+ mLayout.cancelAnimationsOnView(bubble);
+
+ mBubbleDraggingOut = bubble;
+ mBubbleDraggingOut.setTranslationZ(Short.MAX_VALUE);
+ }
+
+ /**
+ * Drags an individual bubble to the given coordinates. Bubbles to the right will animate to
+ * take its place once it's dragged out of the row of bubbles, and animate out of the way if the
+ * bubble is dragged back into the row.
+ */
+ public void dragBubbleOut(View bubbleView, float x, float y) {
+ bubbleView.setTranslationX(x);
+ bubbleView.setTranslationY(y);
+
+ final boolean draggedOutEnough =
+ y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx;
+ if (draggedOutEnough != mBubbleDraggedOutEnough) {
+ animateStackByBubbleWidthsStartingFrom(
+ /* numBubbleWidths */ draggedOutEnough ? -1 : 0,
+ /* startIndex */ mLayout.indexOfChild(bubbleView) + 1);
+ mBubbleDraggedOutEnough = draggedOutEnough;
+ }
+ }
+
+ /**
+ * Snaps a bubble back to its position within the bubble row, and animates the rest of the
+ * bubbles to accommodate it if it was previously dragged out past the threshold.
+ */
+ public void snapBubbleBack(View bubbleView, float velX, float velY) {
+ final int index = mLayout.indexOfChild(bubbleView);
+
+ // Snap the bubble back, respecting its current velocity.
+ mLayout.animateValueForChildAtIndex(
+ DynamicAnimation.TRANSLATION_X, index, getXForChildAtIndex(index), velX);
+ mLayout.animateValueForChildAtIndex(
+ DynamicAnimation.TRANSLATION_Y, index, getExpandedY(), velY);
+ mLayout.setEndListenerForProperties(
+ mLayout.new OneTimeMultiplePropertyEndListener() {
+ @Override
+ void onAllAnimationsForPropertiesEnd() {
+ // Reset Z translation once the bubble is done snapping back.
+ bubbleView.setTranslationZ(0f);
+ }
+ },
+ DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+ animateStackByBubbleWidthsStartingFrom(
+ /* numBubbleWidths */ 0, /* startIndex */ index + 1);
+
+ mBubbleDraggingOut = null;
+ mBubbleDraggedOutEnough = false;
+ }
+
+ /**
+ * Sets configuration variables so that when the given bubble is removed, the animations are
+ * started with the given velocities.
+ */
+ public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) {
+ mBubbleDraggingOut = bubbleView;
+ mBubbleDraggingOutVelX = velX;
+ mBubbleDraggingOutVelY = velY;
+ mBubbleDraggedOutEnough = false;
+ }
+
+ /**
+ * Animates the bubbles, starting at the given index, to the left or right by the given number
+ * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal
+ * positions.
+ */
+ private void animateStackByBubbleWidthsStartingFrom(int numBubbleWidths, int startIndex) {
+ for (int i = startIndex; i < mLayout.getChildCount(); i++) {
+ mLayout.animateValueForChildAtIndex(
+ DynamicAnimation.TRANSLATION_X,
+ i,
+ getXForChildAtIndex(i + numBubbleWidths));
+ }
+ }
+
/** The Y value of the row of expanded bubbles. */
private float getExpandedY() {
final WindowInsets insets = mLayout.getRootWindowInsets();
if (insets != null) {
return mBubblePaddingPx + Math.max(
- insets.getSystemWindowInsetTop(),
+ mStatusBarHeight,
insets.getDisplayCutout() != null
? insets.getDisplayCutout().getSafeInsetTop()
: 0);
@@ -161,12 +265,7 @@
child.setTranslationX(getXForChildAtIndex(index));
child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY());
-
- // Animate the remaining bubbles to the correct X position.
- for (int i = index + 1; i < mLayout.getChildCount(); i++) {
- mLayout.animateValueForChildAtIndex(
- DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
- }
+ animateBubblesAfterIndexToCorrectX(index);
}
@Override
@@ -175,16 +274,36 @@
// TODO: Reverse this when bubbles are at the bottom.
mLayout.animateValueForChild(
DynamicAnimation.ALPHA, child, 0f, finishRemoval);
- mLayout.animateValueForChild(
- DynamicAnimation.TRANSLATION_Y,
- child,
- getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
- // Animate the remaining bubbles to the correct X position.
- for (int i = index; i < mLayout.getChildCount(); i++) {
- mLayout.animateValueForChildAtIndex(
- DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
+ // If we're removing the dragged-out bubble, that means it got dismissed.
+ if (child.equals(mBubbleDraggingOut)) {
+ // Throw it to the bottom of the screen, towards the center horizontally.
+ mLayout.animateValueForChild(
+ DynamicAnimation.TRANSLATION_X,
+ child,
+ mLayout.getWidth() / 2f - mBubbleSizePx / 2f,
+ mBubbleDraggingOutVelX);
+ mLayout.animateValueForChild(
+ DynamicAnimation.TRANSLATION_Y,
+ child,
+ mLayout.getHeight() + mBubbleSizePx,
+ mBubbleDraggingOutVelY);
+
+ // Scale it down a bit so it looks like it's disappearing.
+ mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_SCALE_PERCENT);
+ mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_SCALE_PERCENT);
+
+ mBubbleDraggingOut = null;
+ } else {
+ // If we're removing some random bubble just throw it off the top.
+ mLayout.animateValueForChild(
+ DynamicAnimation.TRANSLATION_Y,
+ child,
+ getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
}
+
+ // Animate all the other bubbles to their new positions sans this bubble.
+ animateBubblesAfterIndexToCorrectX(index);
}
@Override
@@ -203,6 +322,23 @@
() -> super.setChildVisibility(child, index, visibility));
}
+ /**
+ * Animates the bubbles after the given index to the X position they should be in according to
+ * {@link #getXForChildAtIndex}.
+ */
+ private void animateBubblesAfterIndexToCorrectX(int start) {
+ for (int i = start; i < mLayout.getChildCount(); i++) {
+ final View bubble = mLayout.getChildAt(i);
+
+ // Don't animate the dragging out bubble, or it'll jump around while being dragged. It
+ // will be snapped to the correct X value after the drag (if it's not dismissed).
+ if (!bubble.equals(mBubbleDraggingOut)) {
+ mLayout.animateValueForChild(
+ DynamicAnimation.TRANSLATION_X, bubble, getXForChildAtIndex(i));
+ }
+ }
+ }
+
/** Returns the appropriate X translation value for a bubble at the given index. */
private float getXForChildAtIndex(int index) {
return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index a4ddbf7..dfdcfc9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -187,6 +187,20 @@
}
/**
+ * Sets an end listener that will be called whenever any of the given properties' animations
+ * end. For example, setting a listener for TRANSLATION_X and TRANSLATION_Y will result in that
+ * listener being called twice - once when all TRANSLATION_X animations end, and again when all
+ * TRANSLATION_Y animations end.
+ */
+ public void setEndListenerForProperties(
+ DynamicAnimation.OnAnimationEndListener endListener,
+ DynamicAnimation.ViewProperty... properties) {
+ for (DynamicAnimation.ViewProperty property : properties) {
+ setEndListenerForProperty(endListener, property);
+ }
+ }
+
+ /**
* Removes the end listener that would have been called when all child animations for a given
* property stopped running.
*/
@@ -197,7 +211,6 @@
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
- setChildrenVisibility();
// Set up animations for the new view, if the controller is set. If it isn't set, we'll be
// setting up animations for all children when setController is called.
@@ -208,6 +221,8 @@
mController.onChildAdded(child, index);
}
+
+ setChildrenVisibility();
}
@Override
@@ -294,6 +309,13 @@
}
}
+ /** Cancels all of the physics animations running on the given view. */
+ public void cancelAnimationsOnView(View view) {
+ for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
+ getAnimationFromView(property, view).cancel();
+ }
+ }
+
/**
* Animates the property of the given child view, then runs the callback provided when the
* animation ends.
@@ -318,6 +340,11 @@
});
}
+ // Set the start velocity if it's something other than the not-set value.
+ if (startVel != Float.MAX_VALUE) {
+ animation.setStartVelocity(startVel);
+ }
+
animation.animateToFinalPosition(value);
}
}
@@ -337,6 +364,14 @@
animateValueForChild(property, view, value, Float.MAX_VALUE, /* after */ null);
}
+ protected void animateValueForChild(
+ DynamicAnimation.ViewProperty property,
+ View view,
+ float value,
+ float startVel) {
+ animateValueForChild(property, view, value, startVel, /* after */ null);
+ }
+
/**
* Animates the property of the child at the given index to the given value, then runs the
* callback provided when the animation ends.
@@ -414,7 +449,13 @@
*/
private SpringAnimation getAnimationAtIndex(
DynamicAnimation.ViewProperty property, int index) {
- return (SpringAnimation) getChildAt(index).getTag(getTagIdForProperty(property));
+ return getAnimationFromView(property, getChildAt(index));
+ }
+
+ /** Retrieves the animation of the given property from the view via the view tag system. */
+ private SpringAnimation getAnimationFromView(
+ DynamicAnimation.ViewProperty property, View view) {
+ return (SpringAnimation) view.getTag(getTagIdForProperty(property));
}
/** Sets up SpringAnimations of the given property for each child view in the layout. */
@@ -528,4 +569,33 @@
}
}
}
+
+ /**
+ * One time end listener that waits for every animation on every given property to finish. At
+ * that point, it calls {@link #onAllAnimationsForPropertiesEnd} and then removes itself as an
+ * end listener from each property.
+ */
+ public abstract class OneTimeMultiplePropertyEndListener
+ implements DynamicAnimation.OnAnimationEndListener {
+ final DynamicAnimation.ViewProperty[] mViewProperties;
+
+ OneTimeMultiplePropertyEndListener(DynamicAnimation.ViewProperty... properties) {
+ mViewProperties = properties;
+ }
+
+ @Override
+ public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+ float velocity) {
+ if (!arePropertiesAnimating(mViewProperties)) {
+ onAllAnimationsForPropertiesEnd();
+
+ for (DynamicAnimation.ViewProperty property : mViewProperties) {
+ removeEndListenerForProperty(property);
+ }
+ }
+ }
+
+ /** Called when every animation for every property has finished. */
+ abstract void onAllAnimationsForPropertiesEnd();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 0c089a75..7dfb21c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -16,15 +16,12 @@
package com.android.systemui.bubbles.animation;
-import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
-import android.view.WindowManager;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FlingAnimation;
@@ -88,9 +85,8 @@
private int mBubbleOffscreen;
/** How far down the screen the stack starts, when there is no pre-existing location. */
private int mStackStartingVerticalOffset;
-
- private Point mDisplaySize;
- private RectF mAllowableStackPositionRegion;
+ /** Height of the status bar. */
+ private float mStatusBarHeight;
@Override
protected void setLayout(PhysicsAnimationLayout layout) {
@@ -103,11 +99,8 @@
mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
mStackStartingVerticalOffset =
res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);
-
- mDisplaySize = new Point();
- WindowManager wm =
- (WindowManager) layout.getContext().getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getSize(mDisplaySize);
+ mStatusBarHeight =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
/**
@@ -203,10 +196,9 @@
*/
public RectF getAllowableStackPositionRegion() {
final WindowInsets insets = mLayout.getRootWindowInsets();
- mAllowableStackPositionRegion = new RectF();
-
+ final RectF allowableRegion = new RectF();
if (insets != null) {
- mAllowableStackPositionRegion.left =
+ allowableRegion.left =
-mBubbleOffscreen
- mBubblePadding
+ Math.max(
@@ -214,7 +206,7 @@
insets.getDisplayCutout() != null
? insets.getDisplayCutout().getSafeInsetLeft()
: 0);
- mAllowableStackPositionRegion.right =
+ allowableRegion.right =
mLayout.getWidth()
- mIndividualBubbleSize
+ mBubbleOffscreen
@@ -222,17 +214,17 @@
- Math.max(
insets.getSystemWindowInsetRight(),
insets.getDisplayCutout() != null
- ? insets.getDisplayCutout().getSafeInsetRight()
- : 0);
+ ? insets.getDisplayCutout().getSafeInsetRight()
+ : 0);
- mAllowableStackPositionRegion.top =
+ allowableRegion.top =
mBubblePadding
+ Math.max(
- insets.getSystemWindowInsetTop(),
+ mStatusBarHeight,
insets.getDisplayCutout() != null
- ? insets.getDisplayCutout().getSafeInsetTop()
- : 0);
- mAllowableStackPositionRegion.bottom =
+ ? insets.getDisplayCutout().getSafeInsetTop()
+ : 0);
+ allowableRegion.bottom =
mLayout.getHeight()
- mIndividualBubbleSize
- mBubblePadding
@@ -243,7 +235,7 @@
: 0);
}
- return mAllowableStackPositionRegion;
+ return allowableRegion;
}
@Override
@@ -287,31 +279,14 @@
@Override
void onChildAdded(View child, int index) {
- // If this is the first child added, position the stack in its starting position.
if (mLayout.getChildCount() == 1) {
- moveStackToStartPosition();
- }
-
- if (mLayout.indexOfChild(child) == 0) {
- child.setTranslationY(mStackPosition.y);
-
- // Pop in the new bubble.
- child.setScaleX(ANIMATE_IN_STARTING_SCALE);
- child.setScaleY(ANIMATE_IN_STARTING_SCALE);
- mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
- mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);
-
- // Fade in the new bubble.
- child.setAlpha(0);
- mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);
-
- // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
- // animate in from this position. Since the animations are chained, when the new bubble
- // flies in from the side, it will push the other ones out of the way.
- float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
- child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
- mLayout.animateValueForChildAtIndex(
- DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
+ // If this is the first child added, position the stack in its starting position before
+ // animating in.
+ moveStackToStartPosition(() -> animateInBubble(child));
+ } else if (mLayout.indexOfChild(child) == 0) {
+ // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble
+ // to the back of the stack, it'll be largely invisible so don't bother animating it in.
+ animateInBubble(child);
}
}
@@ -334,10 +309,14 @@
}
/** Moves the stack, without any animation, to the starting position. */
- private void moveStackToStartPosition() {
- mLayout.post(() -> setStackPosition(
- getAllowableStackPositionRegion().right,
- getAllowableStackPositionRegion().top + mStackStartingVerticalOffset));
+ private void moveStackToStartPosition(Runnable after) {
+ // Post to ensure that the layout's width and height have been calculated.
+ mLayout.post(() -> {
+ setStackPosition(
+ getAllowableStackPositionRegion().right,
+ getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
+ after.run();
+ });
}
/**
@@ -379,6 +358,29 @@
}
}
+ /** Animates in the given bubble. */
+ private void animateInBubble(View child) {
+ child.setTranslationY(mStackPosition.y);
+
+ // Pop in the new bubble.
+ child.setScaleX(ANIMATE_IN_STARTING_SCALE);
+ child.setScaleY(ANIMATE_IN_STARTING_SCALE);
+ mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
+ mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);
+
+ // Fade in the new bubble.
+ child.setAlpha(0);
+ mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);
+
+ // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
+ // animate in from this position. Since the animations are chained, when the new bubble
+ // flies in from the side, it will push the other ones out of the way.
+ float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
+ child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
+ mLayout.animateValueForChildAtIndex(
+ DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
+ }
+
/**
* Springs the first bubble to the given final position, with the rest of the stack 'following'.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 2f19630..a6af82a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import java.util.stream.Stream;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index a3beb96..57d0588 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
import android.annotation.NonNull;
import android.content.Context;
@@ -26,7 +26,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import javax.inject.Inject;
import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
index 4944732..52b8cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -49,7 +49,7 @@
context.display.getSize(mTmpSize)
val renderScript = RenderScript.create(context)
- val rect = Rect(0, 0,artwork.width, artwork.height)
+ val rect = Rect(0, 0, artwork.width, artwork.height)
MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
val inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
true /* filter */)
@@ -67,6 +67,7 @@
input.destroy()
output.destroy()
inBitmap.recycle()
+ blur.destroy()
val canvas = Canvas(outBitmap)
canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 5605f3d..f6d3cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.notification;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.app.Notification;
import android.service.notification.StatusBarNotification;
@@ -30,7 +30,7 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -102,8 +102,7 @@
* @param entry entry to add
* @param inflatedFlags flags representing content views that were inflated
*/
- private void showAlertingView(NotificationEntry entry,
- @NotificationInflater.InflationFlag int inflatedFlags) {
+ private void showAlertingView(NotificationEntry entry, @InflationFlag int inflatedFlags) {
if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 1ed671f..a5a6d87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -20,7 +20,7 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
/**
* Listener interface for changes sent by NotificationEntryManager.
@@ -65,8 +65,7 @@
/**
* Called when a notification's views are inflated for the first time.
*/
- default void onEntryInflated(NotificationEntry entry,
- @NotificationInflater.InflationFlag int inflatedFlags) {
+ default void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 56922be..3fbc641 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,8 +36,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
@@ -56,7 +56,7 @@
*/
public class NotificationEntryManager implements
Dumpable,
- NotificationInflater.InflationCallback,
+ NotificationContentInflater.InflationCallback,
NotificationUpdateHandler,
VisualStabilityManager.Callback {
private static final String TAG = "NotificationEntryMgr";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index f1bb0d7..6f5baf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -19,8 +19,8 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
import android.content.Context;
@@ -42,8 +42,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -78,7 +78,7 @@
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
private HeadsUpManager mHeadsUpManager;
- private NotificationInflater.InflationCallback mInflationCallback;
+ private NotificationContentInflater.InflationCallback mInflationCallback;
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
@@ -105,7 +105,7 @@
public void setUpWithPresenter(NotificationPresenter presenter,
NotificationListContainer listContainer,
HeadsUpManager headsUpManager,
- NotificationInflater.InflationCallback inflationCallback,
+ NotificationContentInflater.InflationCallback inflationCallback,
BindRowCallback bindRowCallback) {
mPresenter = presenter;
mListContainer = listContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index f6d4ce22..3bf4d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -54,8 +54,8 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
import java.util.ArrayList;
import java.util.Collections;
@@ -515,7 +515,7 @@
if (row != null) row.resetUserExpansion();
}
- public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) {
+ public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
if (row != null) row.freeContentViewWhenSafe(inflationFlag);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 69828c1..2b643d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,13 +17,13 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -89,7 +89,7 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationMediaTemplateViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -134,7 +134,7 @@
}
private LayoutListener mLayoutListener;
- private final NotificationInflater mNotificationInflater;
+ private final NotificationContentInflater mNotificationInflater;
private int mIconTransformContentShift;
private int mIconTransformContentShiftNoIcon;
private int mMaxHeadsUpHeightBeforeN;
@@ -1616,7 +1616,7 @@
}
@VisibleForTesting
- public NotificationInflater getNotificationInflater() {
+ public NotificationContentInflater getNotificationInflater() {
return mNotificationInflater;
}
@@ -1631,7 +1631,7 @@
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
mFalsingManager = FalsingManager.getInstance(context);
- mNotificationInflater = new NotificationInflater(this);
+ mNotificationInflater = new NotificationContentInflater(this);
mMenuRow = new NotificationMenuRow(mContext);
mImageResolver = new NotificationInlineImageResolver(context,
new NotificationInlineImageCache());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 42ebfce..b34907d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -55,9 +55,9 @@
/**
* A utility that inflates the right kind of contentView based on the state
*/
-public class NotificationInflater {
+public class NotificationContentInflater {
- public static final String TAG = "NotificationInflater";
+ public static final String TAG = "NotifContentInflater";
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
@@ -127,7 +127,7 @@
private boolean mRedactAmbient;
private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
- public NotificationInflater(ExpandableNotificationRow row) {
+ public NotificationContentInflater(ExpandableNotificationRow row) {
mRow = row;
}
@@ -232,8 +232,7 @@
* will reinflate it.
*
* @param reInflateFlags flags which views should be inflated. Should be a subset of
- * {@link NotificationInflater#mInflationFlags} as only those will be
- * inflated/reinflated.
+ * {@link #mInflationFlags} as only those will be inflated/reinflated.
*/
private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
if (mRow.isRemoved()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index a5411ec..6eb376b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -52,7 +52,7 @@
@Override
public void preload(Uri uri) {
PreloadImageTask newTask = new PreloadImageTask(mResolver);
- newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri);
+ newTask.executeOnExecutor(NotificationContentInflater.EXECUTOR, uri);
mCache.put(uri, newTask);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4c06ff6..3808702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -26,12 +26,15 @@
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.TransformState;
@@ -108,6 +111,11 @@
return false;
}
+ // Apps targeting Q should fix their dark mode bugs.
+ if (mRow.getEntry().targetSdk >= Build.VERSION_CODES.Q) {
+ return false;
+ }
+
int background = getBackgroundColor(view);
if (background == Color.TRANSPARENT) {
background = defaultBackgroundColor;
@@ -138,17 +146,19 @@
}
}
- private boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
+ @VisibleForTesting
+ boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
if (viewGroup == null) {
return false;
}
+ int backgroundColor = getBackgroundColor(viewGroup);
+ if (Color.alpha(backgroundColor) != 255) {
+ backgroundColor = ContrastColorUtil.compositeColors(backgroundColor, parentBackground);
+ backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 255);
+ }
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
- int backgroundColor = getBackgroundColor(viewGroup);
- if (backgroundColor == Color.TRANSPARENT) {
- backgroundColor = parentBackground;
- }
if (child instanceof TextView) {
int foreground = ((TextView) child).getCurrentTextColor();
if (ColorUtils.calculateContrast(foreground, backgroundColor) < 3) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 6410860..195d02d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -191,7 +191,7 @@
}
protected int adjustDisableFlags(int state) {
- if (!mStatusBarComponent.isLaunchTransitionFadingAway()
+ if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
&& !mKeyguardMonitor.isKeyguardFadingAway()
&& shouldHideNotificationIcons()) {
state |= DISABLE_NOTIFICATION_ICONS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index dae4da7..af442d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -16,9 +16,17 @@
package com.android.systemui.statusbar.phone;
+import android.animation.ObjectAnimator;
import android.annotation.NonNull;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.util.FloatProperty;
+import android.util.MathUtils;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
@@ -27,6 +35,71 @@
public class NavigationBarEdgePanel extends View {
private static final String TAG = "NavigationBarEdgePanel";
+ // TODO: read from resources once drawing is finalized.
+ private static final boolean SHOW_PROTECTION_STROKE = true;
+ private static final int PROTECTION_COLOR = 0xffc0c0c0;
+ private static final int STROKE_COLOR = 0xffe5e5e5;
+ private static final int PROTECTION_WIDTH_PX = 4;
+ private static final int BASE_EXTENT = 32;
+ private static final int ARROW_HEIGHT_DP = 32;
+ private static final int POINT_EXTENT_DP = 8;
+ private static final int ARROW_THICKNESS_DP = 4;
+ private static final float TRACK_LENGTH_MULTIPLIER = 1.5f;
+ private static final float START_POINTING_RATIO = 0.3f;
+ private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f;
+ private static final int ANIM_DURATION_MS = 150;
+
+ private final Paint mPaint = new Paint();
+ private final Paint mProtectionPaint = new Paint();
+
+ private final ObjectAnimator mEndAnimator;
+ private final ObjectAnimator mLegAnimator;
+
+ private final float mDensity;
+ private final float mBaseExtent;
+ private final float mPointExtent;
+ private final float mHeight;
+ private final float mStrokeThickness;
+ private final boolean mIsLeftPanel;
+
+ private float mStartY;
+ private float mStartX;
+
+ private boolean mGestureDetected;
+ private boolean mArrowsPointLeft;
+ private float mGestureLength;
+ private float mLegProgress;
+ private float mDragProgress;
+
+ // How much the "legs" of the back arrow have proceeded from being a line to an arrow.
+ private static final FloatProperty<NavigationBarEdgePanel> LEG_PROGRESS =
+ new FloatProperty<NavigationBarEdgePanel>("legProgress") {
+ @Override
+ public void setValue(NavigationBarEdgePanel object, float value) {
+ object.setLegProgress(value);
+ }
+
+ @Override
+ public Float get(NavigationBarEdgePanel object) {
+ return object.getLegProgress();
+ }
+ };
+
+ // How far across the view the arrow should be drawn.
+ private static final FloatProperty<NavigationBarEdgePanel> DRAG_PROGRESS =
+ new FloatProperty<NavigationBarEdgePanel>("dragProgress") {
+
+ @Override
+ public void setValue(NavigationBarEdgePanel object, float value) {
+ object.setDragProgress(value);
+ }
+
+ @Override
+ public Float get(NavigationBarEdgePanel object) {
+ return object.getDragProgress();
+ }
+ };
+
public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
int gravity) {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
@@ -40,13 +113,43 @@
lp.setTitle(TAG + context.getDisplayId());
lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel);
lp.windowAnimations = 0;
- NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context);
+ NavigationBarEdgePanel panel = new NavigationBarEdgePanel(
+ context, (gravity & Gravity.LEFT) != 0);
panel.setLayoutParams(lp);
return panel;
}
- private NavigationBarEdgePanel(Context context) {
+ private NavigationBarEdgePanel(Context context, boolean isLeftPanel) {
super(context);
+
+ mEndAnimator = ObjectAnimator.ofFloat(this, DRAG_PROGRESS, 1f);
+ mEndAnimator.setAutoCancel(true);
+ mEndAnimator.setDuration(ANIM_DURATION_MS);
+
+ mLegAnimator = ObjectAnimator.ofFloat(this, LEG_PROGRESS, 1f);
+ mLegAnimator.setAutoCancel(true);
+ mLegAnimator.setDuration(ANIM_DURATION_MS);
+
+ mDensity = context.getResources().getDisplayMetrics().density;
+
+ mBaseExtent = dp(BASE_EXTENT);
+ mHeight = dp(ARROW_HEIGHT_DP);
+ mPointExtent = dp(POINT_EXTENT_DP);
+ mStrokeThickness = dp(ARROW_THICKNESS_DP);
+
+ mPaint.setStrokeWidth(mStrokeThickness);
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+ mPaint.setColor(STROKE_COLOR);
+ mPaint.setAntiAlias(true);
+
+ mProtectionPaint.setStrokeWidth(mStrokeThickness + PROTECTION_WIDTH_PX);
+ mProtectionPaint.setStrokeCap(Paint.Cap.ROUND);
+ mProtectionPaint.setColor(PROTECTION_COLOR);
+ mProtectionPaint.setAntiAlias(true);
+
+ // Both panels arrow point the same way
+ mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
+ mIsLeftPanel = isLeftPanel;
}
public void setWindowFlag(int flags, boolean enable) {
@@ -62,6 +165,58 @@
updateLayout(lp);
}
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN : {
+ show(event.getX(), event.getY());
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ handleNewSwipePoint(event.getX());
+ break;
+ }
+ // Fall through
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ hide();
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ float edgeOffset = mBaseExtent * mDragProgress - mStrokeThickness;
+ float animatedOffset = mPointExtent * mLegProgress;
+ canvas.save();
+ canvas.translate(
+ mIsLeftPanel ? edgeOffset : getWidth() - edgeOffset,
+ mStartY - mHeight * 0.5f);
+
+ float outsideX = mArrowsPointLeft ? animatedOffset : 0;
+ float middleX = mArrowsPointLeft ? 0 : animatedOffset;
+
+ if (SHOW_PROTECTION_STROKE) {
+ canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mProtectionPaint);
+ canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mProtectionPaint);
+ }
+
+ canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mPaint);
+ canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mPaint);
+ canvas.restore();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ // TODO: read the gesture length from the nav controller.
+ mGestureLength = getWidth();
+ }
+
public void setDimensions(int width, int height) {
final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
if (lp.width != width || lp.height != height) {
@@ -71,8 +226,72 @@
}
}
+ private void setLegProgress(float progress) {
+ mLegProgress = progress;
+ invalidate();
+ }
+
+ private float getLegProgress() {
+ return mLegProgress;
+ }
+
+ private void setDragProgress(float dragProgress) {
+ mDragProgress = dragProgress;
+ invalidate();
+ }
+
+ private float getDragProgress() {
+ return mDragProgress;
+ }
+
+ private void hide() {
+ animate().alpha(0f).setDuration(ANIM_DURATION_MS);
+ }
+
+ private void show(float x, float y) {
+ mEndAnimator.cancel();
+ mLegAnimator.cancel();
+ setLegProgress(0f);
+ setDragProgress(0f);
+ setAlpha(1f);
+
+ float halfHeight = mHeight * 0.5f;
+ mStartY = MathUtils.constrain(y, halfHeight, getHeight() - halfHeight);
+ mStartX = x;
+ }
+
+ private void handleNewSwipePoint(float x) {
+ float dist = MathUtils.abs(x - mStartX);
+
+ setDragProgress(MathUtils.constrainedMap(
+ 0, 1.0f,
+ 0, mGestureLength * TRACK_LENGTH_MULTIPLIER,
+ dist));
+
+ if (dist < mGestureLength) {
+ setLegProgress(MathUtils.constrainedMap(
+ 0f, POINTEDNESS_BEFORE_SNAP_RATIO,
+ mGestureLength * START_POINTING_RATIO, mGestureLength,
+ dist));
+
+ mGestureDetected = false;
+ } else {
+ if (!mGestureDetected) {
+ performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ mGestureDetected = true;
+
+ mLegAnimator.setFloatValues(1f);
+ mLegAnimator.start();
+ }
+ }
+ }
+
private void updateLayout(WindowManager.LayoutParams lp) {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
wm.updateViewLayout(this, lp);
}
+
+ private float dp(float dp) {
+ return mDensity * dp;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index ebd4204..8152206 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -1227,10 +1227,11 @@
.getSystemService(Context.WINDOW_SERVICE);
int width = mPrototypeController.getEdgeSensitivityWidth();
int height = mPrototypeController.getEdgeSensitivityHeight();
+ // Explicitly left and right, not start and end as this is device relative.
mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
- Gravity.START | Gravity.BOTTOM);
+ Gravity.LEFT | Gravity.BOTTOM);
mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
- Gravity.END | Gravity.BOTTOM);
+ Gravity.RIGHT | Gravity.BOTTOM);
mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());
@@ -1252,15 +1253,12 @@
mButtonDispatchers.valueAt(i).onDestroy();
}
- if (mPrototypeController.isEnabled()) {
- WindowManager wm = (WindowManager) getContext()
- .getSystemService(Context.WINDOW_SERVICE);
- if (mLeftEdgePanel != null) {
- wm.removeView(mLeftEdgePanel);
- }
- if (mRightEdgePanel != null) {
- wm.removeView(mRightEdgePanel);
- }
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ if (mLeftEdgePanel != null) {
+ wm.removeView(mLeftEdgePanel);
+ }
+ if (mRightEdgePanel != null) {
+ wm.removeView(mRightEdgePanel);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index b613e8e..4dbd854 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -34,8 +34,8 @@
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
import com.android.systemui.statusbar.policy.HeadsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 653ec50..30d5b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -117,7 +117,6 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.widget.DateTimeView;
-import android.widget.ImageView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
@@ -460,9 +459,6 @@
protected boolean mDozing;
private boolean mDozingRequested;
- protected BackDropView mBackdrop;
- protected ImageView mBackdropFront, mBackdropBack;
-
private NotificationMediaManager mMediaManager;
protected NotificationLockscreenUserManager mLockscreenUserManager;
protected NotificationRemoteInputManager mRemoteInputManager;
@@ -492,7 +488,6 @@
};
private Runnable mLaunchTransitionEndRunnable;
- protected boolean mLaunchTransitionFadingAway;
private NotificationEntry mDraggedDownEntry;
private boolean mLaunchCameraOnScreenTurningOn;
private boolean mLaunchCameraOnFinishedGoingToSleep;
@@ -932,11 +927,9 @@
mHeadsUpManager, mNotificationIconAreaController, mScrimController);
mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
- mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
- mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
- mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
- mMediaManager.setup(mBackdrop, mBackdropFront, mBackdropBack,
- mScrimController, mLockscreenWallpaper);
+ BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
+ mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
+ backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
// Other icons
mVolumeComponent = getComponent(VolumeComponent.class);
@@ -1592,10 +1585,6 @@
return mPulsing;
}
- public boolean isLaunchTransitionFadingAway() {
- return mLaunchTransitionFadingAway;
- }
-
public boolean hideStatusBarIconsWhenExpanded() {
return mNotificationPanel.hideStatusBarIconsWhenExpanded();
}
@@ -1892,6 +1881,8 @@
mStatusBarWindow.cancelExpandHelper();
mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
+ } else {
+ mBubbleController.collapseStack();
}
}
@@ -2532,6 +2523,9 @@
if (mRemoteInputManager.getController() != null) {
mRemoteInputManager.getController().closeRemoteInputs();
}
+ if (mBubbleController.isStackExpanded()) {
+ mBubbleController.collapseStack();
+ }
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
String reason = intent.getStringExtra("reason");
@@ -2545,6 +2539,9 @@
if (mStatusBarWindowController != null) {
mStatusBarWindowController.setNotTouchable(false);
}
+ if (mBubbleController.isStackExpanded()) {
+ mBubbleController.collapseStack();
+ }
finishBarAnimations();
resetUserExpandedStates();
}
@@ -2966,7 +2963,7 @@
public void showKeyguardImpl() {
mIsKeyguard = true;
- if (mLaunchTransitionFadingAway) {
+ if (mKeyguardMonitor.isLaunchTransitionFadingAway()) {
mNotificationPanel.animate().cancel();
onLaunchTransitionFadingEnded();
}
@@ -2998,7 +2995,7 @@
mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
- mLaunchTransitionFadingAway = false;
+ mKeyguardMonitor.setLaunchTransitionFadingAway(false);
mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
}
@@ -3024,7 +3021,6 @@
mLaunchTransitionEndRunnable = endRunnable;
Runnable hideRunnable = () -> {
mKeyguardMonitor.setLaunchTransitionFadingAway(true);
- mLaunchTransitionFadingAway = true;
if (beforeFading != null) {
beforeFading.run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fd3f680..0461057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,7 +31,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e73c70b..efb4ff0 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -68,6 +68,13 @@
</intent-filter>
</receiver>
+ <activity android:name="com.android.systemui.bubbles.BubblesTestActivity"
+ android:allowEmbedded="true"
+ android:documentLaunchMode="always"
+ android:excludeFromRecents="true"
+ android:exported="false"
+ android:resizeableActivity="true" />
+
<provider
android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index d9315f9..ca72602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -82,6 +82,8 @@
@Mock
private BubbleController.BubbleExpandListener mBubbleExpandListener;
+ private BubbleData mBubbleData;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -104,7 +106,9 @@
when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null);
- mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+ mBubbleData = new BubbleData();
+ mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
+ mBubbleData);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -112,9 +116,6 @@
verify(mNotificationEntryManager, atLeastOnce())
.addNotificationEntryListener(mEntryListenerCaptor.capture());
mEntryListener = mEntryListenerCaptor.getValue();
-
- // Reset the data
- BubbleData.getInstance().clear();
}
@Test
@@ -300,8 +301,8 @@
static class TestableBubbleController extends BubbleController {
TestableBubbleController(Context context,
- StatusBarWindowController statusBarWindowController) {
- super(context, statusBarWindowController);
+ StatusBarWindowController statusBarWindowController, BubbleData data) {
+ super(context, statusBarWindowController, data);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
new file mode 100644
index 0000000..ea472da
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.systemui.R;
+
+/**
+ * Referenced by NotificationTestHelper#makeBubbleMetadata
+ */
+public class BubblesTestActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index c0aac7e..3bd582f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -22,6 +22,8 @@
import android.graphics.PointF;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.FrameLayout;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -63,15 +65,13 @@
public void testExpansionAndCollapse() throws InterruptedException {
Runnable afterExpand = Mockito.mock(Runnable.class);
mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
-
waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
- testExpanded();
+ testBubblesInCorrectExpandedPositions();
Mockito.verify(afterExpand).run();
Runnable afterCollapse = Mockito.mock(Runnable.class);
mExpandedController.collapseBackToStack(afterCollapse);
-
waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1);
@@ -79,17 +79,70 @@
}
@Test
- public void testOnChildRemoved() throws InterruptedException {
- Runnable afterExpand = Mockito.mock(Runnable.class);
- mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
+ public void testOnChildAdded() throws InterruptedException {
+ expand();
+
+ // Add another new view and wait for its animation.
+ final View newView = new FrameLayout(getContext());
+ mLayout.addView(newView, 0);
waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
- testExpanded();
+
+ testBubblesInCorrectExpandedPositions();
+ }
+
+ @Test
+ public void testOnChildRemoved() throws InterruptedException {
+ expand();
// Remove some views and see if the remaining child views still pass the expansion test.
mLayout.removeView(mViews.get(0));
mLayout.removeView(mViews.get(3));
waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
- testExpanded();
+ testBubblesInCorrectExpandedPositions();
+ }
+
+ @Test
+ public void testBubbleDraggedNotDismissedSnapsBack() throws InterruptedException {
+ expand();
+
+ final View draggedBubble = mViews.get(0);
+ mExpandedController.prepareForBubbleDrag(draggedBubble);
+ mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+ assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+ assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+ // Snap it back and make sure it made it back correctly.
+ mExpandedController.snapBubbleBack(draggedBubble, 0f, 0f);
+ waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+ testBubblesInCorrectExpandedPositions();
+ }
+
+ @Test
+ public void testBubbleDismissed() throws InterruptedException {
+ expand();
+
+ final View draggedBubble = mViews.get(0);
+ mExpandedController.prepareForBubbleDrag(draggedBubble);
+ mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+ assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+ assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+ // Snap it back and make sure it made it back correctly.
+ mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f);
+ mLayout.removeView(draggedBubble);
+ waitForLayoutMessageQueue();
+ waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+ assertEquals(-1, mLayout.indexOfChild(draggedBubble));
+ testBubblesInCorrectExpandedPositions();
+ }
+
+ /** Expand the stack and wait for animations to finish. */
+ private void expand() throws InterruptedException {
+ mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class));
+ waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
}
/** Check that children are in the correct positions for being stacked. */
@@ -108,7 +161,7 @@
}
/** Check that children are in the correct positions for being expanded. */
- private void testExpanded() {
+ private void testBubblesInCorrectExpandedPositions() {
// Check all the visible bubbles to see if they're in the right place.
for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) {
assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index 31e44d7..d94b669 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -155,6 +155,11 @@
}
@Override
+ public void cancelAnimationsOnView(View view) {
+ mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view));
+ }
+
+ @Override
protected void animateValueForChildAtIndex(DynamicAnimation.ViewProperty property,
int index, float value, float startVel, Runnable after) {
mMainThreadHandler.post(() ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 660f853..7b1253a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 0b24c21..3c919a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -36,10 +36,11 @@
import android.widget.RemoteViews;
import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -278,7 +279,7 @@
entry.channel.setBlockableSystem(true);
row.setEntry(entry);
row.getNotificationInflater().addInflationFlags(extraInflationFlags);
- NotificationInflaterTest.runThenWaitForInflation(
+ NotificationContentInflaterTest.runThenWaitForInflation(
() -> row.inflateViews(),
row.getNotificationInflater());
@@ -290,7 +291,8 @@
}
private Notification.BubbleMetadata makeBubbleMetadata() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Intent target = new Intent(mContext, BubblesTestActivity.class);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0);
return new Notification.BubbleMetadata.Builder()
.setIntent(bubbleIntent)
.setTitle("bubble title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 79bc0a3..04e7cab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -68,8 +68,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -143,7 +143,7 @@
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
- @NotificationInflater.InflationFlag int inflatedFlags) {
+ @InflationFlag int inflatedFlags) {
super.onAsyncInflationFinished(entry, inflatedFlags);
mCountDownLatch.countDown();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 6d35539..f6fb416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,9 +18,9 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
similarity index 85%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 648df3c..dfaa76a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.notification.row;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -49,6 +49,7 @@
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
import org.junit.Assert;
import org.junit.Before;
@@ -64,9 +65,9 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
-public class NotificationInflaterTest extends SysuiTestCase {
+public class NotificationContentInflaterTest extends SysuiTestCase {
- private NotificationInflater mNotificationInflater;
+ private NotificationContentInflater mNotificationInflater;
private Notification.Builder mBuilder;
private ExpandableNotificationRow mRow;
@@ -80,8 +81,8 @@
ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
mBuilder.build());
mRow = spy(row);
- mNotificationInflater = new NotificationInflater(mRow);
- mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+ mNotificationInflater = new NotificationContentInflater(mRow);
+ mNotificationInflater.setInflationCallback(new InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
Exception e) {
@@ -89,7 +90,7 @@
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
- @NotificationInflater.InflationFlag int inflatedFlags) {
+ @NotificationContentInflater.InflationFlag int inflatedFlags) {
}
});
}
@@ -158,14 +159,14 @@
@Test
@Ignore
public void testInflationIsRetriedIfAsyncFails() throws Exception {
- NotificationInflater.InflationProgress result =
- new NotificationInflater.InflationProgress();
+ NotificationContentInflater.InflationProgress result =
+ new NotificationContentInflater.InflationProgress();
result.packageContext = mContext;
CountDownLatch countDownLatch = new CountDownLatch(1);
- NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
+ NotificationContentInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
true /* isNewView */, (v, p, r) -> true,
- new NotificationInflater.InflationCallback() {
+ new InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
Exception e) {
@@ -175,11 +176,11 @@
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
- @NotificationInflater.InflationFlag int inflatedFlags) {
+ @NotificationContentInflater.InflationFlag int inflatedFlags) {
countDownLatch.countDown();
}
}, mRow.getPrivateLayout(), null, null, new HashMap<>(),
- new NotificationInflater.ApplyCallback() {
+ new NotificationContentInflater.ApplyCallback() {
@Override
public void setResultView(View v) {
}
@@ -199,8 +200,8 @@
mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, true);
mNotificationInflater.updateNeedsRedaction(true);
- NotificationInflater.AsyncInflationTask asyncInflationTask =
- (NotificationInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
+ NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+ (NotificationContentInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
assertEquals(FLAG_CONTENT_VIEW_AMBIENT | FLAG_CONTENT_VIEW_PUBLIC,
asyncInflationTask.getReInflateFlags());
asyncInflationTask.abort();
@@ -217,8 +218,8 @@
mNotificationInflater.setIsChildInGroup(true);
InflationTask runningTask = mRow.getEntry().getRunningTask();
- NotificationInflater.AsyncInflationTask asyncInflationTask =
- (NotificationInflater.AsyncInflationTask) runningTask;
+ NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+ (NotificationContentInflater.AsyncInflationTask) runningTask;
assertEquals("Successive inflations don't inherit the previous flags!",
FLAG_CONTENT_VIEW_ALL, asyncInflationTask.getReInflateFlags());
runningTask.abort();
@@ -233,19 +234,19 @@
R.layout.custom_view_dark));
RemoteViews decoratedMediaView = mBuilder.createContentView();
Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
- NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
+ NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
}
public static void runThenWaitForInflation(Runnable block,
- NotificationInflater inflater) throws Exception {
+ NotificationContentInflater inflater) throws Exception {
runThenWaitForInflation(block, false /* expectingException */, inflater);
}
private static void runThenWaitForInflation(Runnable block, boolean expectingException,
- NotificationInflater inflater) throws Exception {
+ NotificationContentInflater inflater) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
final ExceptionHolder exceptionHolder = new ExceptionHolder();
- inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+ inflater.setInflationCallback(new InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
Exception e) {
@@ -257,7 +258,7 @@
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
- @NotificationInflater.InflationFlag int inflatedFlags) {
+ @NotificationContentInflater.InflationFlag int inflatedFlags) {
if (expectingException) {
exceptionHolder.setException(new RuntimeException(
"Inflation finished even though there should be an error"));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 24aa772..637b30c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import static org.mockito.Mockito.mock;
import android.content.Context;
import android.support.test.filters.SmallTest;
@@ -22,13 +24,15 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.util.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,12 +41,26 @@
@RunWithLooper
public class NotificationViewWrapperTest extends SysuiTestCase {
- @Test
- public void constructor_doesntUseViewContext() throws Exception {
+ private View mView;
+ private ExpandableNotificationRow mRow;
+ private TestableNotificationViewWrapper mNotificationViewWrapper;
+
+ @Before
+ public void setup() throws Exception {
Assert.sMainLooper = TestableLooper.get(this).getLooper();
- new TestableNotificationViewWrapper(mContext,
- new View(mContext),
- new NotificationTestHelper(getContext()).createRow());
+ mView = mock(View.class);
+ mRow = new NotificationTestHelper(getContext()).createRow();
+ mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
+ }
+
+ @Test
+ public void childrenNeedInversion_doesntCrash_whenOpacity() {
+ LinearLayout viewGroup = new LinearLayout(mContext);
+ TextView textView = new TextView(mContext);
+ textView.setTextColor(0xcc000000);
+ viewGroup.addView(textView);
+
+ mNotificationViewWrapper.childrenNeedInversion(0xcc000000, viewGroup);
}
static class TestableNotificationViewWrapper extends NotificationViewWrapper {
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 17e9b35..00cb6d3 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -47,6 +47,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
+import com.android.server.backup.utils.FileUtils;
+import com.android.server.backup.utils.RandomAccessFileUtils;
import java.io.File;
import java.io.FileDescriptor;
@@ -89,6 +91,12 @@
*/
private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
+ /**
+ * Name of file for non-system users that remembers whether backup was explicitly activated or
+ * deactivated with a call to setBackupServiceActive.
+ */
+ private static final String REMEMBER_ACTIVATED_FILENAME_PREFIX = "backup-remember-activated";
+
// Product-level suppression of backup/restore.
private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
@@ -134,11 +142,17 @@
}
/** Stored in the system user's directory and the file is indexed by the user it refers to. */
- protected File getActivatedFileForNonSystemUser(int userId) {
- return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
- BACKUP_ACTIVATED_FILENAME + "-" + userId);
+ protected File getRememberActivatedFileForNonSystemUser(int userId) {
+ return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir(
+ REMEMBER_ACTIVATED_FILENAME_PREFIX, userId));
}
+ /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
+ }
+
+ // TODO (b/124359804) move to util method in FileUtils
private void createFile(File file) throws IOException {
if (file.exists()) {
return;
@@ -150,6 +164,7 @@
}
}
+ // TODO (b/124359804) move to util method in FileUtils
private void deleteFile(File file) {
if (!file.exists()) {
return;
@@ -312,6 +327,19 @@
public void setBackupServiceActive(int userId, boolean makeActive) {
enforcePermissionsOnUser(userId);
+ // In Q, backup is OFF by default for non-system users. In the future, we will change that
+ // to ON unless backup was explicitly deactivated with a (permissioned) call to
+ // setBackupServiceActive.
+ // Therefore, remember this for use in the future. Basically the default in the future will
+ // be: rememberFile.exists() ? rememberFile.value() : ON
+ // Note that this has to be done right after the permission checks and before any other
+ // action since we need to remember that a permissioned call was made irrespective of
+ // whether the call changes the state or not.
+ if (userId != UserHandle.USER_SYSTEM) {
+ RandomAccessFileUtils.writeBoolean(getRememberActivatedFileForNonSystemUser(userId),
+ makeActive);
+ }
+
if (mGlobalDisable) {
Slog.i(TAG, "Backup service not supported");
return;
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
index aabd41a..4638ac6 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -48,4 +48,9 @@
// is a staging dir, we dont need to copy below dir to new system user dir
return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
}
+
+ /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+ static File getStateFileInSystemDir(String prefix, int userId) {
+ return new File(getBaseStateDir(UserHandle.USER_SYSTEM), prefix + "-" + userId);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index b2afbc3..d4ac731 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -130,6 +130,7 @@
import com.android.server.backup.utils.AppBackupUtils;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
+import com.android.server.backup.utils.FileUtils;
import com.android.server.backup.utils.SparseArrayUtils;
import com.google.android.collect.Sets;
@@ -2319,6 +2320,7 @@
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"setAncestralSerialNumber");
Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber);
+ // TODO (b/124359804)
try (RandomAccessFile af = getAncestralSerialNumberFile()) {
af.writeLong(ancestralSerialNumber);
} catch (IOException e) {
@@ -2331,6 +2333,7 @@
* {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set.
*/
public long getAncestralSerialNumber() {
+ // TODO (b/124359804)
try (RandomAccessFile af = getAncestralSerialNumberFile()) {
return af.readLong();
} catch (IOException e) {
@@ -2344,13 +2347,7 @@
mAncestralSerialNumberFile = new File(
UserBackupManagerFiles.getBaseStateDir(getUserId()),
SERIAL_ID_FILE);
- if (!mAncestralSerialNumberFile.exists()) {
- try {
- mAncestralSerialNumberFile.createNewFile();
- } catch (IOException e) {
- Slog.w(TAG, "serial number mapping file creation failed", e);
- }
- }
+ FileUtils.createNewFile(mAncestralSerialNumberFile);
}
return new RandomAccessFile(mAncestralSerialNumberFile, "rwd");
}
diff --git a/services/backup/java/com/android/server/backup/utils/FileUtils.java b/services/backup/java/com/android/server/backup/utils/FileUtils.java
new file mode 100644
index 0000000..00686cb
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/FileUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+/** Utility methods useful for working with backup related files. */
+public final class FileUtils {
+ /**
+ * Ensure that the file exists in the file system. If an IOException is thrown, it is ignored.
+ * This method is useful to avoid code duplication of the "try-catch-ignore exception" block.
+ */
+ public static File createNewFile(File file) {
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to create file:" + file.getAbsolutePath(), e);
+ }
+ return file;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java
new file mode 100644
index 0000000..abf906a
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/** Utility methods useful for working with backup related RandomAccessFiles. */
+public final class RandomAccessFileUtils {
+ private static RandomAccessFile getRandomAccessFile(File file) throws FileNotFoundException {
+ return new RandomAccessFile(file, "rwd");
+ }
+
+ /** Write a boolean to a File by wrapping it using a RandomAccessFile. */
+ public static void writeBoolean(File file, boolean b) {
+ try (RandomAccessFile af = getRandomAccessFile(file)) {
+ af.writeBoolean(b);
+ } catch (IOException e) {
+ Slog.w(TAG, "Error writing file:" + file.getAbsolutePath(), e);
+ }
+ }
+
+ /** Read a boolean from a File by wrapping it using a RandomAccessFile. */
+ public static boolean readBoolean(File file, boolean def) {
+ try (RandomAccessFile af = getRandomAccessFile(file)) {
+ return af.readBoolean();
+ } catch (IOException e) {
+ Slog.w(TAG, "Error reading file:" + file.getAbsolutePath(), e);
+ }
+ return def;
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dad428a..915c131 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,8 +57,10 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.net.CaptivePortal;
import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
+import android.net.ICaptivePortal;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
@@ -86,6 +88,7 @@
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
@@ -917,7 +920,8 @@
mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
- //set up the listener for user state for creating user VPNs
+ // Set up the listener for user state for creating user VPNs.
+ // Should run on mHandler to avoid any races.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -925,7 +929,11 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
@@ -936,7 +944,11 @@
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
try {
mNMS.registerObserver(mTethering);
@@ -2690,11 +2702,6 @@
EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
mNai.network.netId));
}
-
- @Override
- public void logCaptivePortalLoginEvent(int eventId, String packageName) {
- new MetricsLogger().action(eventId, packageName);
- }
}
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -3249,22 +3256,63 @@
/**
* NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
* endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+ * @param network Network on which the captive portal was detected.
* @param appExtras Bundle to use as intent extras for the captive portal application.
* Must be treated as opaque to avoid preventing the captive portal app to
* update its arguments.
*/
@Override
- public void startCaptivePortalAppInternal(Bundle appExtras) {
+ public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
appIntent.putExtras(appExtras);
+ appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+ new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
Binder.withCleanCallingIdentity(() ->
mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
}
+ private class CaptivePortalImpl extends ICaptivePortal.Stub {
+ private final Network mNetwork;
+
+ private CaptivePortalImpl(Network network) {
+ mNetwork = network;
+ }
+
+ @Override
+ public void appResponse(final int response) throws RemoteException {
+ if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
+ enforceSettingsPermission();
+ }
+
+ // getNetworkAgentInfoForNetwork is thread-safe
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
+ if (nai == null) return;
+
+ // nai.networkMonitor() is thread-safe
+ final INetworkMonitor nm = nai.networkMonitor();
+ if (nm == null) return;
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ nm.notifyCaptivePortalAppFinished(response);
+ } finally {
+ // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void logEvent(int eventId, String packageName) {
+ enforceSettingsPermission();
+
+ new MetricsLogger().action(eventId, packageName);
+ }
+ }
+
public boolean avoidBadWifi() {
return mMultinetworkPolicyTracker.getAvoidBadWifi();
}
@@ -4099,17 +4147,27 @@
* handler thread through their agent, this is asynchronous. When the capabilities objects
* are computed they will be up-to-date as they are computed synchronously from here and
* this is running on the ConnectivityService thread.
- * TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
*/
private void updateAllVpnsCapabilities() {
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
final Vpn vpn = mVpns.valueAt(i);
- vpn.updateCapabilities();
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
+ private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
+ ensureRunningOnConnectivityServiceThread();
+ NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
+ if (vpnNai == null || nc == null) {
+ return;
+ }
+ updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
+ }
+
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4450,22 +4508,28 @@
private void onUserAdded(int userId) {
mPermissionMonitor.onUserAdded(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserAdded(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
private void onUserRemoved(int userId) {
mPermissionMonitor.onUserRemoved(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserRemoved(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
@@ -4534,6 +4598,7 @@
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ ensureRunningOnConnectivityServiceThread();
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -5043,6 +5108,19 @@
return getNetworkForRequest(mDefaultRequest.requestId);
}
+ @Nullable
+ private Network getNetwork(@Nullable NetworkAgentInfo nai) {
+ return nai != null ? nai.network : null;
+ }
+
+ private void ensureRunningOnConnectivityServiceThread() {
+ if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on ConnectivityService thread: "
+ + Thread.currentThread().getName());
+ }
+ }
+
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
return nai == getDefaultNetwork();
}
@@ -5099,7 +5177,7 @@
if (DBG) log("registerNetworkAgent " + nai);
final long token = Binder.clearCallingIdentity();
try {
- mContext.getSystemService(NetworkStack.class).makeNetworkMonitor(
+ getNetworkStack().makeNetworkMonitor(
toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai));
} finally {
Binder.restoreCallingIdentity(token);
@@ -5111,6 +5189,11 @@
return nai.network.netId;
}
+ @VisibleForTesting
+ protected NetworkStackClient getNetworkStack() {
+ return NetworkStackClient.getInstance();
+ }
+
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
nai.onNetworkMonitorCreated(networkMonitor);
if (VDBG) log("Got NetworkAgent Messenger");
@@ -5669,6 +5752,8 @@
updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
notifyIfacesChangedForNetworkStats();
+ // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
+ updateAllVpnsCapabilities();
}
private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -6108,6 +6193,10 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ if (networkAgent.isVPN()) {
+ updateAllVpnsCapabilities();
+ }
+
// Consider network even though it is not yet validated.
final long now = SystemClock.elapsedRealtime();
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
@@ -6369,7 +6458,11 @@
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
- mHandler.post(() -> notifyIfacesChangedForNetworkStats());
+ mHandler.post(() -> {
+ // Update VPN's capabilities based on updated underlying network set.
+ updateAllVpnsCapabilities();
+ notifyIfacesChangedForNetworkStats();
+ });
}
return success;
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0b0934b..9d92ea2b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1645,36 +1645,6 @@
}
}
- private abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
- protected final CallerIdentity mCallerIdentity;
- protected final String mListenerName;
-
- private LinkedListenerBase(@NonNull CallerIdentity callerIdentity,
- @NonNull String listenerName) {
- mCallerIdentity = callerIdentity;
- mListenerName = listenerName;
- }
- }
-
- private static class LinkedListener<TListener> extends LinkedListenerBase {
- private final TListener mListener;
- private final Consumer<TListener> mBinderDeathCallback;
-
- private LinkedListener(@NonNull TListener listener, String listenerName,
- @NonNull CallerIdentity callerIdentity,
- @NonNull Consumer<TListener> binderDeathCallback) {
- super(callerIdentity, listenerName);
- mListener = listener;
- mBinderDeathCallback = binderDeathCallback;
- }
-
- @Override
- public void binderDied() {
- if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
- mBinderDeathCallback.accept(mListener);
- }
- }
-
@Override
public void removeGnssBatchingCallback() {
synchronized (mLock) {
@@ -2711,77 +2681,85 @@
@Override
public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) {
- if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) {
- return false;
- }
-
- CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
- Binder.getCallingPid(), packageName);
- LinkedListener<IGnssStatusListener> linkedListener = new LinkedListener<>(listener,
- "GnssStatusListener", callerIdentity, this::unregisterGnssStatusCallback);
- IBinder binder = listener.asBinder();
- synchronized (mLock) {
- if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
- return false;
- }
-
- mGnssStatusListeners.put(binder, linkedListener);
- long identity = Binder.clearCallingIdentity();
- try {
- if (isThrottlingExemptLocked(callerIdentity)
- || isImportanceForeground(
- mActivityManager.getPackageImportance(packageName))) {
- mGnssStatusProvider.addListener(listener, callerIdentity);
- }
- return true;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ return addGnssDataListener(listener, packageName, "GnssStatusListener",
+ mGnssStatusProvider, mGnssStatusListeners,
+ this::unregisterGnssStatusCallback);
}
@Override
public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
- if (mGnssStatusProvider == null) {
- return;
- }
-
- IBinder binder = listener.asBinder();
- synchronized (mLock) {
- LinkedListener<IGnssStatusListener> linkedListener =
- mGnssStatusListeners.remove(binder);
- if (linkedListener == null) {
- return;
- }
- unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
- mGnssStatusProvider.removeListener(listener);
- }
+ removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners);
}
@Override
public boolean addGnssMeasurementsListener(
IGnssMeasurementsListener listener, String packageName) {
- if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
+ return addGnssDataListener(listener, packageName, "GnssMeasurementsListener",
+ mGnssMeasurementsProvider, mGnssMeasurementsListeners,
+ this::removeGnssMeasurementsListener);
+ }
+
+ @Override
+ public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
+ removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners);
+ }
+
+ private abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
+ protected final CallerIdentity mCallerIdentity;
+ protected final String mListenerName;
+
+ private LinkedListenerBase(@NonNull CallerIdentity callerIdentity,
+ @NonNull String listenerName) {
+ mCallerIdentity = callerIdentity;
+ mListenerName = listenerName;
+ }
+ }
+
+ private static class LinkedListener<TListener> extends LinkedListenerBase {
+ private final TListener mListener;
+ private final Consumer<TListener> mBinderDeathCallback;
+
+ private LinkedListener(@NonNull TListener listener, String listenerName,
+ @NonNull CallerIdentity callerIdentity,
+ @NonNull Consumer<TListener> binderDeathCallback) {
+ super(callerIdentity, listenerName);
+ mListener = listener;
+ mBinderDeathCallback = binderDeathCallback;
+ }
+
+ @Override
+ public void binderDied() {
+ if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
+ mBinderDeathCallback.accept(mListener);
+ }
+ }
+
+ private <TListener extends IInterface> boolean addGnssDataListener(
+ TListener listener, String packageName, String listenerName,
+ RemoteListenerHelper<TListener> gnssDataProvider,
+ ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
+ Consumer<TListener> binderDeathCallback) {
+ if (!hasGnssPermissions(packageName) || gnssDataProvider == null) {
return false;
}
CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
Binder.getCallingPid(), packageName);
- LinkedListener<IGnssMeasurementsListener> linkedListener = new LinkedListener<>(listener,
- "GnssMeasurementsListener", callerIdentity, this::removeGnssMeasurementsListener);
+ LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
+ listenerName, callerIdentity, binderDeathCallback);
IBinder binder = listener.asBinder();
synchronized (mLock) {
if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
return false;
}
- mGnssMeasurementsListeners.put(binder, linkedListener);
+ gnssDataListeners.put(binder, linkedListener);
long identity = Binder.clearCallingIdentity();
try {
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
mActivityManager.getPackageImportance(packageName))) {
- mGnssMeasurementsProvider.addListener(listener, callerIdentity);
+ gnssDataProvider.addListener(listener, callerIdentity);
}
return true;
} finally {
@@ -2790,25 +2768,24 @@
}
}
- @Override
- public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
- if (mGnssMeasurementsProvider == null) {
+ private <TListener extends IInterface> void removeGnssDataListener(
+ TListener listener, RemoteListenerHelper<TListener> gnssDataProvider,
+ ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
+ if (gnssDataProvider == null) {
return;
}
IBinder binder = listener.asBinder();
synchronized (mLock) {
- LinkedListener<IGnssMeasurementsListener> linkedListener =
- mGnssMeasurementsListeners.remove(binder);
+ LinkedListener<TListener> linkedListener = gnssDataListeners.remove(binder);
if (linkedListener == null) {
return;
}
unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
- mGnssMeasurementsProvider.removeListener(listener);
+ gnssDataProvider.removeListener(listener);
}
}
-
private boolean linkToListenerDeathNotificationLocked(IBinder binder,
LinkedListenerBase linkedListener) {
try {
@@ -2864,52 +2841,15 @@
@Override
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener, String packageName) {
- if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
- return false;
- }
-
- CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
- Binder.getCallingPid(), packageName);
- LinkedListener<IGnssNavigationMessageListener> linkedListener =
- new LinkedListener<>(listener, "GnssNavigationMessageListener", callerIdentity,
- this::removeGnssNavigationMessageListener);
- IBinder binder = listener.asBinder();
- synchronized (mLock) {
- if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
- return false;
- }
-
- mGnssNavigationMessageListeners.put(binder, linkedListener);
- long identity = Binder.clearCallingIdentity();
- try {
- if (isThrottlingExemptLocked(callerIdentity)
- || isImportanceForeground(
- mActivityManager.getPackageImportance(packageName))) {
- mGnssNavigationMessageProvider.addListener(listener, callerIdentity);
- }
- return true;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ return addGnssDataListener(listener, packageName, "GnssNavigationMessageListener",
+ mGnssNavigationMessageProvider, mGnssNavigationMessageListeners,
+ this::removeGnssNavigationMessageListener);
}
@Override
public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
- if (mGnssNavigationMessageProvider == null) {
- return;
- }
-
- IBinder binder = listener.asBinder();
- synchronized (mLock) {
- LinkedListener<IGnssNavigationMessageListener> linkedListener =
- mGnssNavigationMessageListeners.remove(binder);
- if (linkedListener == null) {
- return;
- }
- unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
- mGnssNavigationMessageProvider.removeListener(listener);
- }
+ removeGnssDataListener(listener, mGnssNavigationMessageProvider,
+ mGnssNavigationMessageListeners);
}
@Override
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 24543b7..236797b 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -477,6 +477,10 @@
mStats.updateRpmStatsLocked();
}
+ if ((updateFlags & UPDATE_RAIL) != 0) {
+ mStats.updateRailStatsLocked();
+ }
+
if (bluetoothInfo != null) {
if (bluetoothInfo.isValid()) {
mStats.updateBluetoothStateLocked(bluetoothInfo);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0890032..4d5cb8c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -59,6 +59,7 @@
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.PowerProfile;
+import com.android.internal.os.RailStats;
import com.android.internal.os.RpmStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.ParseUtils;
@@ -84,7 +85,8 @@
*/
public final class BatteryStatsService extends IBatteryStats.Stub
implements PowerManagerInternal.LowPowerModeListener,
- BatteryStatsImpl.PlatformIdleStateCallback {
+ BatteryStatsImpl.PlatformIdleStateCallback,
+ BatteryStatsImpl.RailEnergyDataCallback {
static final String TAG = "BatteryStatsService";
static final boolean DBG = false;
@@ -98,6 +100,7 @@
private native void getLowPowerStats(RpmStats rpmStats);
private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
private native int getSubsystemLowPowerStats(ByteBuffer outBuffer);
+ private native void getRailEnergyPowerStats(RailStats railStats);
private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
@@ -121,6 +124,16 @@
}
@Override
+ public void fillRailDataStats(RailStats railStats) {
+ if (DBG) Slog.d(TAG, "begin getRailEnergyPowerStats");
+ try {
+ getRailEnergyPowerStats(railStats);
+ } finally {
+ if (DBG) Slog.d(TAG, "end getRailEnergyPowerStats");
+ }
+ }
+
+ @Override
public String getPlatformLowPowerStats() {
if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
try {
@@ -177,7 +190,8 @@
return (umi != null) ? umi.getUserIds() : null;
}
};
- mStats = new BatteryStatsImpl(systemDir, handler, this, mUserManagerUserInfoProvider);
+ mStats = new BatteryStatsImpl(systemDir, handler, this,
+ this, mUserManagerUserInfoProvider);
mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
@@ -1460,7 +1474,8 @@
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
- null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+ null, mStats.mHandler, null, null,
+ mUserManagerUserInfoProvider);
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpProtoLocked(
@@ -1498,7 +1513,8 @@
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
- null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+ null, mStats.mHandler, null, null,
+ mUserManagerUserInfoProvider);
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 93a71e5..4e03b72 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1692,7 +1692,8 @@
(app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
app.curAdj == ProcessList.HOME_APP_ADJ)) {
mAppCompact.compactAppSome(app);
- } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+ } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+ || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
&& app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
mAppCompact.compactAppFull(app);
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 0b6a432..bbe4ed1 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -177,13 +177,12 @@
* Handle boot phase PHASE_ACTIVITY_MANAGER_READY.
*/
private void onPhaseActivityManagerReady() {
+ // RoleManager doesn't tell us about upgrade, so we still need to listen for app upgrades.
+ // (app uninstall/disable will be notified by RoleManager.)
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
packageFilter.addDataScheme("package");
- packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
packageFilter, null, mHandler);
@@ -256,14 +255,6 @@
handlePackageAddedReplacing(packageName, userId);
}
break;
- case Intent.ACTION_PACKAGE_REMOVED:
- if (!replacing) {
- handlePackageRemoved(packageName, userId);
- }
- break;
- case Intent.ACTION_PACKAGE_CHANGED:
- handlePackageChanged(packageName, userId);
- break;
}
}
};
@@ -371,31 +362,6 @@
}
}
- private void handlePackageRemoved(String packageName, int userId) {
- if (DEBUG) {
- Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName);
- }
- synchronized (mLock) {
- final AppServiceFinder finder = findFinderLocked(userId, packageName);
- if (finder != null) {
- unbindServicesLocked(userId, finder, "package uninstall");
- }
- }
- }
-
- private void handlePackageChanged(String packageName, int userId) {
- if (DEBUG) {
- Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName);
- }
- synchronized (mLock) {
- final AppServiceFinder finder = findFinderLocked(userId, packageName);
- if (finder != null) {
- unbindServicesLocked(userId, finder, "package changed");
- bindServicesLocked(userId, finder, "package changed");
- }
- }
- }
-
private void rebindAllLocked(String reason) {
for (int i = 0; i < mRunningUsers.size(); i++) {
if (!mRunningUsers.valueAt(i)) {
diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
index 4c5f1a1..753d3b0 100644
--- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
@@ -16,14 +16,10 @@
package com.android.server.appbinding.finders;
-import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL;
-
import android.Manifest.permission;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.IBinder;
@@ -35,7 +31,8 @@
import android.util.Slog;
import com.android.internal.R;
-import com.android.internal.telephony.SmsApplication;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
import com.android.server.appbinding.AppBindingConstants;
import java.util.function.BiConsumer;
@@ -45,10 +42,15 @@
*/
public class CarrierMessagingClientServiceFinder
extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> {
+
+ private final RoleManager mRoleManager;
+
public CarrierMessagingClientServiceFinder(Context context,
BiConsumer<AppServiceFinder, Integer> listener,
Handler callbackHandler) {
super(context, listener, callbackHandler);
+
+ mRoleManager = context.getSystemService(RoleManager.class);
}
@Override
@@ -84,9 +86,8 @@
@Override
public String getTargetPackage(int userId) {
- final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(
- mContext, /* updateIfNeeded= */ true, userId);
- String ret = cn == null ? null : cn.getPackageName();
+ final String ret = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser(
+ RoleManager.ROLE_SMS, UserHandle.of(userId)));
if (DEBUG) {
Slog.d(TAG, "getTargetPackage()=" + ret);
@@ -97,9 +98,8 @@
@Override
public void startMonitoring() {
- final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
- mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter,
- /* permission= */ null, mHandler);
+ mRoleManager.addOnRoleHoldersChangedListenerAsUser(
+ mContext.getMainExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
}
@Override
@@ -118,12 +118,11 @@
return constants.SMS_APP_BIND_FLAGS;
}
- private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) {
- mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId());
- }
+ private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> {
+ if (RoleManager.ROLE_SMS.equals(role)) {
+ BackgroundThread.getHandler().post(() -> {
+ mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier());
+ });
}
};
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 9a1d7bf..f293328 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -201,11 +201,6 @@
StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
error);
}
-
- @Override
- public IBinder asBinder() {
- return null;
- }
});
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Cannot call into the AttentionService");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5ad6d2f..1a62d4f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3222,6 +3222,21 @@
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
}
+
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ synchronized (mSetModeDeathHandlers) {
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.getMode() == AudioSystem.MODE_IN_CALL) {
+ Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ }
+ }
+ }
+
// for logging only
final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index bd4acdb..7d4ac59 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -34,7 +34,7 @@
private long mOpId;
public abstract int handleFailedAttempt();
- public abstract void resetFailedAttempts();
+ public void resetFailedAttempts() {}
public static final int LOCKOUT_NONE = 0;
public static final int LOCKOUT_TIMED = 1;
@@ -42,6 +42,11 @@
private final boolean mRequireConfirmation;
+ // We need to track this state since it's possible for applications to request for
+ // authentication while the device is already locked out. In that case, the client is created
+ // but not started yet. The user shouldn't receive the error haptics in this case.
+ private boolean mStarted;
+
/**
* This method is called when authentication starts.
*/
@@ -53,6 +58,11 @@
*/
public abstract void onStop();
+ /**
+ * @return true if the framework should handle lockout.
+ */
+ public abstract boolean shouldFrameworkHandleLockout();
+
public AuthenticationClient(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
@@ -91,6 +101,23 @@
}
@Override
+ public boolean onError(long deviceId, int error, int vendorCode) {
+ if (!shouldFrameworkHandleLockout()) {
+ switch (error) {
+ case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
+ case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
+ if (mStarted) {
+ vibrateError();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return super.onError(deviceId, error, vendorCode);
+ }
+
+ @Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
super.logOnAuthenticated(authenticated, mRequireConfirmation, getTargetUserId(),
@@ -113,7 +140,9 @@
vibrateSuccess();
}
result = true;
- resetFailedAttempts();
+ if (shouldFrameworkHandleLockout()) {
+ resetFailedAttempts();
+ }
onStop();
final byte[] byteToken = new byte[token.size()];
@@ -147,9 +176,10 @@
if (listener != null) {
vibrateError();
}
+
// Allow system-defined limit of number of attempts before giving up
final int lockoutMode = handleFailedAttempt();
- if (lockoutMode != LOCKOUT_NONE) {
+ if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) {
Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
+ lockoutMode + ")");
stop(false);
@@ -170,7 +200,7 @@
}
}
}
- result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
+ result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
}
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception", e);
@@ -184,6 +214,7 @@
*/
@Override
public int start() {
+ mStarted = true;
onStart();
try {
final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
@@ -209,6 +240,8 @@
return 0;
}
+ mStarted = false;
+
onStop();
try {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index bca84f7..ddd416e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -273,16 +273,13 @@
*/
private static final int STATE_AUTH_STARTED = 2;
/**
- * Authentication is paused, waiting for the user to press "try again" button. Since the
- * try again button requires us to cancel authentication, this represents the state where
- * ERROR_CANCELED is not received yet.
+ * Authentication is paused, waiting for the user to press "try again" button. Only
+ * passive modalities such as Face or Iris should have this state. Note that for passive
+ * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
+ * fingerprint.
*/
private static final int STATE_AUTH_PAUSED = 3;
/**
- * Same as above, except the ERROR_CANCELED has been received.
- */
- private static final int STATE_AUTH_PAUSED_CANCELED = 4;
- /**
* Authentication is successful, but we're waiting for the user to press "confirm" button.
*/
private static final int STATE_AUTH_PENDING_CONFIRM = 5;
@@ -457,11 +454,6 @@
// Pause authentication. onBiometricAuthenticated(false) causes the
// dialog to show a "try again" button for passive modalities.
mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
- // Cancel authentication. Skip the token/package check since we are
- // cancelling from system server. The interface is permission protected so
- // this is fine.
- cancelInternal(null /* token */, null /* package */,
- false /* fromClient */);
}
mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
@@ -507,24 +499,15 @@
}
}, BiometricPrompt.HIDE_DIALOG_DELAY);
}
- } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
- || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) {
- if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
- && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- // Skip the first ERROR_CANCELED message when this happens, since
- // "try again" requires us to cancel authentication but keep
- // the prompt showing.
- mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED;
- } else {
- // In the "try again" state, we should forward canceled errors to
- // the client and and clean up.
- mCurrentAuthSession.mClientReceiver.onError(error, message);
- mStatusBarService.onBiometricError(message);
- mActivityTaskManager.unregisterTaskStackListener(
- mTaskStackListener);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- }
+ } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
+ // In the "try again" state, we should forward canceled errors to
+ // the client and and clean up.
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(
+ mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
} else {
Slog.e(TAG, "Impossible session error state: "
+ mCurrentAuthSession.mState);
@@ -705,8 +688,7 @@
if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
final boolean continuing = mCurrentAuthSession != null &&
- (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
- || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED);
+ (mCurrentAuthSession.mState == STATE_AUTH_PAUSED);
mCurrentAuthSession = mPendingAuthSession;
mPendingAuthSession = null;
@@ -1029,7 +1011,7 @@
}
@Override // Binder call
- public void resetTimeout(byte[] token) {
+ public void resetLockout(byte[] token) {
checkInternalPermission();
final long ident = Binder.clearCallingIdentity();
try {
@@ -1037,7 +1019,7 @@
mFingerprintService.resetTimeout(token);
}
if (mFaceService != null) {
- mFaceService.resetTimeout(token);
+ mFaceService.resetLockout(token);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 9e0f2fc..92a8d93 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -20,17 +20,12 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.IActivityTaskManager;
-import android.app.PendingIntent;
import android.app.SynchronousUserSwitchObserver;
import android.app.TaskStackListener;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -54,14 +49,11 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import android.util.StatsLog;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
-import com.android.server.biometrics.fingerprint.FingerprintService;
import java.util.ArrayList;
import java.util.Collections;
@@ -80,38 +72,36 @@
protected static final boolean DEBUG = true;
+ private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
private static final int MSG_USER_SWITCHING = 10;
- private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
private final Context mContext;
private final String mKeyguardPackage;
- private final SparseBooleanArray mTimedLockoutCleared;
- private final SparseIntArray mFailedAttempts;
private final IActivityTaskManager mActivityTaskManager;
- private final AlarmManager mAlarmManager;
private final PowerManager mPowerManager;
private final UserManager mUserManager;
private final MetricsLogger mMetricsLogger;
private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
- private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
protected final IStatusBarService mStatusBarService;
protected final Map<Integer, Long> mAuthenticatorIds =
Collections.synchronizedMap(new HashMap<>());
- protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
- new ResetFailedAttemptsForUserRunnable();
protected final AppOpsManager mAppOps;
protected final H mHandler = new H();
+ private final IBinder mToken = new Binder(); // Used for internal enumeration
+ private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
+
private IBiometricService mBiometricService;
private ClientMonitor mCurrentClient;
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
protected int mCurrentUserId = UserHandle.USER_NULL;
+ protected long mHalDeviceId;
// Tracks if the current authentication makes use of CryptoObjects.
protected boolean mIsCrypto;
// Normal authentications are tracked by mPerformanceMap.
@@ -135,23 +125,16 @@
protected abstract String getTag();
/**
+ * @return wrapper for the HAL
+ */
+ protected abstract DaemonWrapper getDaemonWrapper();
+
+ /**
* @return the biometric utilities for a specific implementation.
*/
protected abstract BiometricUtils getBiometricUtils();
/**
- * @return the number of failed attempts after which the user will be temporarily locked out
- * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
- */
- protected abstract int getFailedAttemptsLockoutTimed();
-
- /**
- * @return the number of failed attempts after which the user will be permanently locked out
- * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
- */
- protected abstract int getFailedAttemptsLockoutPermanent();
-
- /**
* @return the metrics constants for a biometric implementation.
*/
protected abstract Metrics getMetrics();
@@ -186,13 +169,6 @@
protected abstract long getHalDeviceId();
/**
- * This method is called when the user switches. Implementations should probably notify the
- * HAL.
- * @param userId
- */
- protected abstract void handleUserSwitching(int userId);
-
- /**
* @param userId
* @return Returns true if the user has any enrolled biometrics.
*/
@@ -215,6 +191,9 @@
*/
protected abstract boolean checkAppOps(int uid, String opPackageName);
+ protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
+ int userId);
+
/**
* Notifies clients of any change in the biometric state (active / idle). This is mainly for
* Fingerprint navigation gestures.
@@ -224,6 +203,11 @@
protected abstract int statsModality();
+ /**
+ * @return one of the AuthenticationClient LOCKOUT constants
+ */
+ protected abstract int getLockoutMode();
+
protected abstract class AuthenticationClientImpl extends AuthenticationClient {
// Used to check if the public API that was invoked was from FingerprintManager. Only
@@ -271,21 +255,12 @@
}
@Override
- public void resetFailedAttempts() {
- resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
-
- @Override
public void notifyUserActivity() {
userActivity();
}
@Override
public int handleFailedAttempt() {
- final int currentUser = ActivityManager.getCurrentUser();
- mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
- mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
final int lockoutMode = getLockoutMode();
if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
mPerformanceStats.permanentLockout++;
@@ -295,7 +270,6 @@
// Failing multiple times will continue to push out the lockout time
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- scheduleLockoutResetForUser(currentUser);
return lockoutMode;
}
return AuthenticationClient.LOCKOUT_NONE;
@@ -319,40 +293,106 @@
}
}
- protected abstract class RemovalClientImpl extends RemovalClient {
- private boolean mShouldNotify;
-
- public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
- IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
+ /**
+ * An internal class to help clean up unknown templates in HAL and Framework
+ */
+ private final class InternalRemovalClient extends RemovalClient {
+ InternalRemovalClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int templateId, int groupId, int userId,
boolean restricted, String owner) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId,
userId, restricted, owner, getBiometricUtils());
}
- public void setShouldNotifyUserActivity(boolean shouldNotify) {
- mShouldNotify = shouldNotify;
- }
-
@Override
- public void notifyUserActivity() {
- if (mShouldNotify) {
- userActivity();
- }
+ protected int statsModality() {
+ return BiometricServiceBase.this.statsModality();
}
}
- protected abstract class EnumerateClientImpl extends EnumerateClient {
+ /**
+ * Internal class to help clean up unknown templates in the HAL and Framework
+ */
+ private final class InternalEnumerateClient extends EnumerateClient {
- public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
- IBinder token, ServiceListener listener, int groupId, int userId,
- boolean restricted, String owner) {
+ private BiometricUtils mUtils;
+ // List of templates that are known to the Framework. Remove from this list when enumerate
+ // returns a template that contains a match.
+ private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
+ // List of templates to remove from the HAL
+ private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
+
+ InternalEnumerateClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int groupId, int userId, boolean restricted,
+ String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
+ BiometricUtils utils) {
super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
restricted, owner);
+ mEnrolledList = enrolledList;
+ mUtils = utils;
+ }
+
+ private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
+ if (identifier == null) {
+ return;
+ }
+ Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
+ boolean matched = false;
+ for (int i = 0; i < mEnrolledList.size(); i++) {
+ if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
+ mEnrolledList.remove(i);
+ matched = true;
+ break;
+ }
+ }
+
+ // TemplateId 0 means no templates in HAL
+ if (!matched && identifier.getBiometricId() != 0) {
+ mUnknownHALTemplates.add(identifier);
+ }
+ Slog.v(getTag(), "Matched: " + matched);
+ }
+
+ private void doTemplateCleanup() {
+ if (mEnrolledList == null) {
+ return;
+ }
+
+ // At this point, mEnrolledList only contains templates known to the framework and
+ // not the HAL.
+ for (int i = 0; i < mEnrolledList.size(); i++) {
+ BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
+ Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: "
+ + identifier.getBiometricId() + " "
+ + identifier.getName());
+ mUtils.removeBiometricForUser(getContext(),
+ getTargetUserId(), identifier.getBiometricId());
+ StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ statsModality(),
+ BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
+ }
+ mEnrolledList.clear();
+ }
+
+ public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
+ return mUnknownHALTemplates;
}
@Override
- public void notifyUserActivity() {
- userActivity();
+ public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ handleEnumeratedTemplate(identifier);
+ if (remaining == 0) {
+ doTemplateCleanup();
+ }
+ return remaining == 0;
+ }
+
+ @Override
+ protected int statsModality() {
+ return BiometricServiceBase.this.statsModality();
}
}
@@ -429,13 +469,14 @@
* subclasses.
*/
protected interface DaemonWrapper {
- int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
+ int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
int authenticate(long operationId, int groupId) throws RemoteException;
int cancel() throws RemoteException;
int remove(int groupId, int biometricId) throws RemoteException;
int enumerate() throws RemoteException;
- int enroll(byte[] cryptoToken, int groupId, int timeout,
+ int enroll(byte[] token, int groupId, int timeout,
ArrayList<Integer> disabledFeatures) throws RemoteException;
+ void resetLockout(byte[] token) throws RemoteException;
}
/**
@@ -506,24 +547,7 @@
}
}
- private final class LockoutReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
- if (getLockoutResetIntent().equals(intent.getAction())) {
- final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
- resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
- }
- }
- }
- private final class ResetFailedAttemptsForUserRunnable implements Runnable {
- @Override
- public void run() {
- resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
- }
private final class LockoutResetMonitor implements IBinder.DeathRecipient {
private static final long WAKELOCK_TIMEOUT_MS = 2000;
@@ -583,6 +607,19 @@
}
/**
+ * Container for enumerated templates. Used to keep track when cleaning up unknown
+ * templates.
+ */
+ private final class UserTemplate {
+ final BiometricAuthenticator.Identifier mIdentifier;
+ final int mUserId;
+ UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
+ this.mIdentifier = identifier;
+ this.mUserId = userId;
+ }
+ }
+
+ /**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
@@ -599,16 +636,11 @@
mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
com.android.internal.R.string.config_keyguardComponent)).getPackageName();
mAppOps = context.getSystemService(AppOpsManager.class);
- mTimedLockoutCleared = new SparseBooleanArray();
- mFailedAttempts = new SparseIntArray();
mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
Context.ACTIVITY_TASK_SERVICE)).getService();
mPowerManager = mContext.getSystemService(PowerManager.class);
- mAlarmManager = mContext.getSystemService(AlarmManager.class);
mUserManager = UserManager.get(mContext);
mMetricsLogger = new MetricsLogger();
- mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
- getLockoutBroadcastPermission(), null /* handler */);
}
@Override
@@ -688,6 +720,11 @@
if (DEBUG) Slog.v(getTag(), "handleError(client="
+ (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
+ if (client instanceof InternalRemovalClient
+ || client instanceof InternalEnumerateClient) {
+ clearEnumerateState();
+ }
+
if (client != null && client.onError(deviceId, error, vendorCode)) {
removeClient(client);
}
@@ -721,6 +758,39 @@
updateActiveGroup(userId, null);
}
}
+
+ if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) {
+ startCleanupUnknownHALTemplates();
+ } else if (client instanceof InternalRemovalClient) {
+ clearEnumerateState();
+ }
+ }
+
+ protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
+ ClientMonitor client = getCurrentClient();
+
+ client.onEnumerationResult(identifier, remaining);
+
+ // All templates in the HAL for this user were enumerated
+ if (remaining == 0) {
+ if (client instanceof InternalEnumerateClient) {
+ List<BiometricAuthenticator.Identifier> unknownHALTemplates =
+ ((InternalEnumerateClient) client).getUnknownHALTemplates();
+
+ if (!unknownHALTemplates.isEmpty()) {
+ Slog.w(getTag(), "Adding " + unknownHALTemplates.size()
+ + " templates for deletion");
+ }
+ for (int i = 0; i < unknownHALTemplates.size(); i++) {
+ mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
+ client.getTargetUserId()));
+ }
+ removeClient(client);
+ startCleanupUnknownHALTemplates();
+ } else {
+ removeClient(client);
+ }
+ }
}
/**
@@ -832,13 +902,13 @@
});
}
- protected void removeInternal(RemovalClientImpl client) {
+ protected void removeInternal(RemovalClient client) {
mHandler.post(() -> {
startClient(client, true /* initiatedByClient */);
});
}
- protected void enumerateInternal(EnumerateClientImpl client) {
+ protected void enumerateInternal(EnumerateClient client) {
mHandler.post(() -> {
startClient(client, true /* initiatedByClient */);
});
@@ -919,19 +989,6 @@
return mKeyguardPackage.equals(clientPackage);
}
- protected int getLockoutMode() {
- final int currentUser = ActivityManager.getCurrentUser();
- final int failedAttempts = mFailedAttempts.get(currentUser, 0);
- if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
- return AuthenticationClient.LOCKOUT_PERMANENT;
- } else if (failedAttempts > 0 &&
- mTimedLockoutCleared.get(currentUser, false) == false
- && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
- return AuthenticationClient.LOCKOUT_TIMED;
- }
- return AuthenticationClient.LOCKOUT_NONE;
- }
-
private boolean isForegroundActivity(int uid, int pid) {
try {
List<ActivityManager.RunningAppProcessInfo> procs =
@@ -965,10 +1022,10 @@
currentClient.getOwnerString());
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
- if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
- currentClient instanceof FingerprintService.RemovalClientImpl) {
+ if (currentClient instanceof InternalEnumerateClient
+ || currentClient instanceof InternalRemovalClient) {
// This condition means we're currently running internal diagnostics to
- // remove extra fingerprints in the hardware and/or the software
+ // remove extra templates in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
if (newClient != null) {
Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
@@ -1124,16 +1181,75 @@
return mAuthenticatorIds.getOrDefault(userId, 0L);
}
- private void scheduleLockoutResetForUser(int userId) {
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
- getLockoutResetIntentForUser(userId));
+ /**
+ * This method should be called upon connection to the daemon, and when user switches.
+ * @param userId
+ */
+ protected void doTemplateCleanupForUser(int userId) {
+ if (CLEANUP_UNKNOWN_TEMPLATES) {
+ enumerateUser(userId);
+ }
}
- private PendingIntent getLockoutResetIntentForUser(int userId) {
- return PendingIntent.getBroadcast(mContext, userId,
- new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ private void clearEnumerateState() {
+ if (DEBUG) Slog.v(getTag(), "clearEnumerateState()");
+ mUnknownHALTemplates.clear();
+ }
+
+ /**
+ * Remove unknown templates from HAL
+ */
+ private void startCleanupUnknownHALTemplates() {
+ if (!mUnknownHALTemplates.isEmpty()) {
+ UserTemplate template = mUnknownHALTemplates.get(0);
+ mUnknownHALTemplates.remove(template);
+ boolean restricted = !hasPermission(getManageBiometricPermission());
+ InternalRemovalClient client = new InternalRemovalClient(getContext(),
+ getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */,
+ template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
+ restricted, getContext().getPackageName());
+ removeInternal(client);
+ StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ statsModality(),
+ BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
+ } else {
+ clearEnumerateState();
+ }
+ }
+
+ private void enumerateUser(int userId) {
+ if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")");
+
+ final boolean restricted = !hasPermission(getManageBiometricPermission());
+ final List<? extends BiometricAuthenticator.Identifier> enrolledList =
+ getEnrolledTemplates(userId);
+
+ InternalEnumerateClient client = new InternalEnumerateClient(getContext(),
+ getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId,
+ userId, restricted, getContext().getOpPackageName(), enrolledList,
+ getBiometricUtils());
+ enumerateInternal(client);
+ }
+
+ /**
+ * This method is called when the user switches. Implementations should probably notify the
+ * HAL.
+ */
+ protected void handleUserSwitching(int userId) {
+ if (getCurrentClient() instanceof InternalRemovalClient
+ || getCurrentClient() instanceof InternalEnumerateClient) {
+ Slog.w(getTag(), "User switched while performing cleanup");
+ removeClient(getCurrentClient());
+ clearEnumerateState();
+ }
+ updateActiveGroup(userId, null);
+ doTemplateCleanupForUser(userId);
+ }
+
+ protected void notifyLockoutResetMonitors() {
+ for (int i = 0; i < mLockoutMonitors.size(); i++) {
+ mLockoutMonitors.get(i).sendLockoutReset();
+ }
}
private void userActivity() {
@@ -1169,25 +1285,6 @@
return userId;
}
- // Attempt counter should only be cleared when Keyguard goes away or when
- // a biometric is successfully authenticated.
- private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
- if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
- }
- if (clearAttemptCounter) {
- mFailedAttempts.put(userId, 0);
- }
- mTimedLockoutCleared.put(userId, true);
- // If we're asked to reset failed attempts externally (i.e. from Keyguard),
- // the alarm might still be pending; remove it.
- cancelLockoutResetForUser(userId);
- notifyLockoutResetMonitors();
- }
-
- private void cancelLockoutResetForUser(int userId) {
- mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
- }
private void listenForUserSwitches() {
try {
@@ -1204,12 +1301,6 @@
}
}
- private void notifyLockoutResetMonitors() {
- for (int i = 0; i < mLockoutMonitors.size(); i++) {
- mLockoutMonitors.get(i).sendLockoutReset();
- }
- }
-
private void removeLockoutResetCallback(
LockoutResetMonitor monitor) {
mLockoutMonitors.remove(monitor);
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 0f57f48..44ac037 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -39,6 +39,10 @@
}
@Override
+ public void notifyUserActivity() {
+ }
+
+ @Override
protected int statsAction() {
return BiometricsProtoEnums.ACTION_ENUMERATE;
}
@@ -94,7 +98,9 @@
public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
int remaining) {
try {
- getListener().onEnumerated(identifier, remaining);
+ if (getListener() != null) {
+ getListener().onEnumerated(identifier, remaining);
+ }
} catch (RemoteException e) {
Slog.w(getLogTag(), "Failed to notify enumerated:", e);
}
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 0509067..a18f336 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -44,6 +44,10 @@
}
@Override
+ public void notifyUserActivity() {
+ }
+
+ @Override
protected int statsAction() {
return BiometricsProtoEnums.ACTION_REMOVE;
}
@@ -95,7 +99,9 @@
private boolean sendRemoved(BiometricAuthenticator.Identifier identifier,
int remaining) {
try {
- getListener().onRemoved(identifier, remaining);
+ if (getListener() != null) {
+ getListener().onRemoved(identifier, remaining);
+ }
} catch (RemoteException e) {
Slog.w(getLogTag(), "Failed to notify Removed:", e);
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index eab3820..d2d1482 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -51,9 +51,13 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
import com.android.server.biometrics.BiometricUtils;
+import com.android.server.biometrics.ClientMonitor;
+import com.android.server.biometrics.EnumerateClient;
import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.RemovalClient;
import org.json.JSONArray;
import org.json.JSONException;
@@ -79,8 +83,6 @@
private static final String FACE_DATA_DIR = "facedata";
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
- private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
- private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
private final class FaceAuthClient extends AuthenticationClientImpl {
@@ -96,6 +98,25 @@
protected int statsModality() {
return FaceService.this.statsModality();
}
+
+ @Override
+ public boolean shouldFrameworkHandleLockout() {
+ return false;
+ }
+
+ @Override
+ public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+ boolean authenticated, ArrayList<Byte> token) {
+ final boolean result = super.onAuthenticated(identifier, authenticated, token);
+
+ // For face, the authentication lifecycle ends either when
+ // 1) Authenticated == true
+ // 2) Error occurred
+ // 3) Authenticated == false
+ // Fingerprint currently does not end when the third condition is met which is a bug,
+ // but let's leave it as-is for now.
+ return result || !authenticated;
+ }
}
/**
@@ -106,6 +127,7 @@
/**
* The following methods contain common code which is shared in biometrics/common.
*/
+
@Override // Binder call
public long generateChallenge(IBinder token) {
checkPermission(MANAGE_BIOMETRIC);
@@ -216,15 +238,14 @@
}
final boolean restricted = isRestricted();
- final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
- mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */,
- userId, restricted, token.toString()) {
+ final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
+ 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
@Override
protected int statsModality() {
return FaceService.this.statsModality();
}
};
- client.setShouldNotifyUserActivity(true);
removeInternal(client);
}
@@ -234,9 +255,9 @@
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = isRestricted();
- final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
- mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
- restricted, getContext().getOpPackageName()) {
+ final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
+ userId, restricted, getContext().getOpPackageName()) {
@Override
protected int statsModality() {
return FaceService.this.statsModality();
@@ -317,7 +338,7 @@
return null;
}
- return FaceService.this.getEnrolledFaces(userId);
+ return FaceService.this.getEnrolledTemplates(userId);
}
@Override // Binder call
@@ -354,10 +375,13 @@
}
@Override // Binder call
- public void resetTimeout(byte[] token) {
+ public void resetLockout(byte[] token) {
checkPermission(MANAGE_BIOMETRIC);
- // TODO: confirm security token when we move timeout management into the HAL layer.
- mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+ try {
+ mDaemonWrapper.resetLockout(token);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to reset lockout", e);
+ }
}
@Override
@@ -511,7 +535,8 @@
public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
throws RemoteException {
if (mFaceServiceReceiver != null) {
-
+ mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(),
+ identifier.getBiometricId(), remaining);
}
}
}
@@ -520,80 +545,107 @@
@GuardedBy("this")
private IBiometricsFace mDaemon;
- private long mHalDeviceId;
+ // One of the AuthenticationClient constants
+ private int mCurrentUserLockoutMode;
/**
* Receives callbacks from the HAL.
*/
private IBiometricsFaceClientCallback mDaemonCallback =
new IBiometricsFaceClientCallback.Stub() {
- @Override
- public void onEnrollResult(final long deviceId, int faceId, int userId,
- int remaining) {
- mHandler.post(() -> {
- final Face face = new Face(getBiometricUtils()
- .getUniqueName(getContext(), userId), faceId, deviceId);
- FaceService.super.handleEnrollResult(face, remaining);
- });
+ @Override
+ public void onEnrollResult(final long deviceId, int faceId, int userId,
+ int remaining) {
+ mHandler.post(() -> {
+ final Face face = new Face(getBiometricUtils()
+ .getUniqueName(getContext(), userId), faceId, deviceId);
+ FaceService.super.handleEnrollResult(face, remaining);
+ });
+ }
+
+ @Override
+ public void onAcquired(final long deviceId, final int userId,
+ final int acquiredInfo,
+ final int vendorCode) {
+ mHandler.post(() -> {
+ FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+ });
+ }
+
+ @Override
+ public void onAuthenticated(final long deviceId, final int faceId, final int userId,
+ ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ Face face = new Face("", faceId, deviceId);
+ FaceService.super.handleAuthenticated(face, token);
+ });
+ }
+
+ @Override
+ public void onError(final long deviceId, final int userId, final int error,
+ final int vendorCode) {
+ mHandler.post(() -> {
+ FaceService.super.handleError(deviceId, error, vendorCode);
+
+ // TODO: this chunk of code should be common to all biometric services
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ // If we get HW_UNAVAILABLE, try to connect again later...
+ Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+ synchronized (this) {
+ mDaemon = null;
+ mHalDeviceId = 0;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
}
+ });
+ }
- @Override
- public void onAcquired(final long deviceId, final int userId,
- final int acquiredInfo,
- final int vendorCode) {
- mHandler.post(() -> {
- FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
- });
+ @Override
+ public void onRemoved(final long deviceId, final int faceId, final int userId,
+ final int remaining) {
+ mHandler.post(() -> {
+ final Face face = new Face("", faceId, deviceId);
+ FaceService.super.handleRemoved(face, remaining);
+ });
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
+ throws RemoteException {
+ mHandler.post(() -> {
+ if (!faceIds.isEmpty()) {
+ for (int i = 0; i < faceIds.size(); i++) {
+ final Face face = new Face("", faceIds.get(i), deviceId);
+ // Convert to old old behavior
+ FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
+ }
+ } else {
+ // For face, the HIDL contract is to receive an empty list when there are no
+ // templates enrolled. Send a null identifier since we don't consume them
+ // anywhere, and send remaining == 0 to plumb this with existing common code.
+ FaceService.super.handleEnumerate(null /* identifier */, 0);
}
+ });
+ }
- @Override
- public void onAuthenticated(final long deviceId, final int faceId, final int userId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- Face face = new Face("", faceId, deviceId);
- FaceService.super.handleAuthenticated(face, token);
- });
+ @Override
+ public void onLockoutChanged(long duration) {
+ Slog.d(TAG, "onLockoutChanged: " + duration);
+ if (duration == 0) {
+ mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
+ } else if (duration == Long.MAX_VALUE) {
+ mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
+ } else {
+ mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
+ }
+
+ mHandler.post(() -> {
+ if (duration == 0) {
+ notifyLockoutResetMonitors();
}
-
- @Override
- public void onError(final long deviceId, final int userId, final int error,
- final int vendorCode) {
- mHandler.post(() -> {
- FaceService.super.handleError(deviceId, error, vendorCode);
-
- // TODO: this chunk of code should be common to all biometric services
- if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
- // If we get HW_UNAVAILABLE, try to connect again later...
- Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
- synchronized (this) {
- mDaemon = null;
- mHalDeviceId = 0;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
- });
- }
-
- @Override
- public void onRemoved(final long deviceId, final int faceId, final int userId,
- final int remaining) {
- mHandler.post(() -> {
- final Face face = new Face("", faceId, deviceId);
- FaceService.super.handleRemoved(face, remaining);
- });
- }
-
- @Override
- public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
- throws RemoteException {
- // TODO
- }
-
- @Override
- public void onLockoutChanged(long duration) {
-
- }
- };
+ });
+ }
+ };
/**
* Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
@@ -652,9 +704,22 @@
for (int i = 0; i < cryptoToken.length; i++) {
token.add(cryptoToken[i]);
}
- // TODO: plumb requireAttention down from framework
return daemon.enroll(token, timeout, disabledFeatures);
}
+
+ @Override
+ public void resetLockout(byte[] cryptoToken) throws RemoteException {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "resetLockout(): no face HAL!");
+ return;
+ }
+ final ArrayList<Byte> token = new ArrayList<>();
+ for (int i = 0; i < cryptoToken.length; i++) {
+ token.add(cryptoToken[i]);
+ }
+ daemon.resetLockout(token);
+ }
};
@@ -675,21 +740,16 @@
}
@Override
+ protected DaemonWrapper getDaemonWrapper() {
+ return mDaemonWrapper;
+ }
+
+ @Override
protected BiometricUtils getBiometricUtils() {
return FaceUtils.getInstance();
}
@Override
- protected int getFailedAttemptsLockoutTimed() {
- return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
- }
-
- @Override
- protected int getFailedAttemptsLockoutPermanent() {
- return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
- }
-
- @Override
protected Metrics getMetrics() {
return mFaceMetrics;
}
@@ -698,7 +758,7 @@
protected boolean hasReachedEnrollmentLimit(int userId) {
final int limit = getContext().getResources().getInteger(
com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
- final int enrolled = FaceService.this.getEnrolledFaces(userId).size();
+ final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
if (enrolled >= limit) {
Slog.w(TAG, "Too many faces registered");
return true;
@@ -766,7 +826,9 @@
@Override
protected void handleUserSwitching(int userId) {
- updateActiveGroup(userId, null);
+ super.handleUserSwitching(userId);
+ // Will be updated when we get the callback from HAL
+ mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
}
@Override
@@ -785,7 +847,6 @@
@Override
protected void checkUseBiometricPermission() {
// noop for Face. The permission checks are all done on the incoming binder call.
- // TODO: Perhaps do the same in FingerprintService
}
@Override
@@ -795,6 +856,11 @@
}
@Override
+ protected List<Face> getEnrolledTemplates(int userId) {
+ return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ }
+
+ @Override
protected void notifyClientActiveCallbacks(boolean isActive) {
// noop for Face.
}
@@ -804,6 +870,11 @@
return BiometricsProtoEnums.MODALITY_FACE;
}
+ @Override
+ protected int getLockoutMode() {
+ return mCurrentUserLockoutMode;
+ }
+
/** Gets the face daemon */
private synchronized IBiometricsFace getFaceDaemon() {
if (mDaemon == null) {
@@ -833,6 +904,7 @@
if (mHalDeviceId != 0) {
loadAuthenticatorIds();
updateActiveGroup(ActivityManager.getCurrentUser(), null);
+ doTemplateCleanupForUser(ActivityManager.getCurrentUser());
} else {
Slog.w(TAG, "Failed to open Face HAL!");
MetricsLogger.count(getContext(), "faced_openhal_error", 1);
@@ -870,10 +942,6 @@
return 0;
}
- private List<Face> getEnrolledFaces(int userId) {
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
- }
-
private void dumpInternal(PrintWriter pw) {
JSONObject dump = new JSONObject();
try {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index d8544e3..164468e 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -24,8 +24,13 @@
import static android.Manifest.permission.USE_FINGERPRINT;
import android.app.ActivityManager;
+import android.app.AlarmManager;
import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -46,21 +51,25 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SELinux;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-import android.util.StatsLog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.ClientMonitor;
import com.android.server.biometrics.EnumerateClient;
import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.RemovalClient;
import org.json.JSONArray;
import org.json.JSONException;
@@ -85,20 +94,30 @@
protected static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
- private static final boolean CLEANUP_UNUSED_FP = true;
private static final String FP_DATA_DIR = "fpdata";
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET";
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+ private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
+ private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
- // TODO: This should be refactored into BiometricService
- private final class UserFingerprint {
- Fingerprint f;
- int userId;
- public UserFingerprint(Fingerprint f, int userId) {
- this.f = f;
- this.userId = userId;
+ private final class ResetFailedAttemptsForUserRunnable implements Runnable {
+ @Override
+ public void run() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+ }
+
+ private final class LockoutReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
+ if (getLockoutResetIntent().equals(intent.getAction())) {
+ final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+ resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
+ }
}
}
@@ -121,6 +140,30 @@
protected int statsModality() {
return FingerprintService.this.statsModality();
}
+
+ @Override
+ public void resetFailedAttempts() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+
+ @Override
+ public boolean shouldFrameworkHandleLockout() {
+ return true;
+ }
+
+ @Override
+ public int handleFailedAttempt() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+ mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
+
+ if (getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+ scheduleLockoutResetForUser(currentUser);
+ }
+
+ return super.handleFailedAttempt();
+ }
}
/**
@@ -241,15 +284,14 @@
}
final boolean restricted = isRestricted();
- final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
- mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId,
- userId, restricted, token.toString()) {
+ final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+ fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) {
@Override
protected int statsModality() {
return FingerprintService.this.statsModality();
}
};
- client.setShouldNotifyUserActivity(true);
removeInternal(client);
}
@@ -259,9 +301,9 @@
checkPermission(MANAGE_FINGERPRINT);
final boolean restricted = isRestricted();
- final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
- mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
- restricted, getContext().getOpPackageName()) {
+ final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
+ userId, restricted, getContext().getOpPackageName()) {
@Override
protected int statsModality() {
return FingerprintService.this.statsModality();
@@ -339,7 +381,7 @@
return Collections.emptyList();
}
- return FingerprintService.this.getEnrolledFingerprints(userId);
+ return FingerprintService.this.getEnrolledTemplates(userId);
}
@Override // Binder call
@@ -445,7 +487,6 @@
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
throws RemoteException {
if (mFingerprintServiceReceiver != null) {
- // TODO: Pass up the fp directly instead
final Fingerprint fp = (Fingerprint) identifier;
mFingerprintServiceReceiver.onEnrollResult(fp.getDeviceId(), fp.getBiometricId(),
fp.getGroupId(), remaining);
@@ -493,7 +534,6 @@
public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)
throws RemoteException {
if (mFingerprintServiceReceiver != null) {
- // TODO: Pass up the fp directly instead
final Fingerprint fp = (Fingerprint) identifier;
mFingerprintServiceReceiver.onRemoved(fp.getDeviceId(), fp.getBiometricId(),
fp.getGroupId(), remaining);
@@ -511,105 +551,18 @@
}
}
- /**
- * An internal class to help clean up unknown fingerprints in the hardware and software.
- */
- private final class InternalEnumerateClient extends BiometricServiceBase.EnumerateClientImpl {
-
- private List<Fingerprint> mEnrolledList;
- private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
-
- public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId,
- IBinder token, ServiceListener listener, int groupId, int userId,
- boolean restricted, String owner, List<Fingerprint> enrolledList) {
- super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted,
- owner);
- mEnrolledList = enrolledList;
- }
-
- private void handleEnumeratedFingerprint(
- BiometricAuthenticator.Identifier identifier, int remaining) {
- boolean matched = false;
- for (int i = 0; i < mEnrolledList.size(); i++) {
- if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
- mEnrolledList.remove(i);
- matched = true;
- break;
- }
- }
-
- // fingerId 0 means no fingerprints are in hardware
- if (!matched && identifier.getBiometricId() != 0) {
- mUnknownFingerprints.add((Fingerprint) identifier);
- }
- }
-
- private void doFingerprintCleanup() {
- if (mEnrolledList == null) {
- return;
- }
-
- for (Fingerprint f : mEnrolledList) {
- Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: "
- + f.getName() + " " + f.getBiometricId() + " " + f.getGroupId()
- + " " + f.getDeviceId());
- FingerprintUtils.getInstance().removeBiometricForUser(getContext(),
- getTargetUserId(), f.getBiometricId());
- StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
- }
- mEnrolledList.clear();
- }
-
- public List<Fingerprint> getUnknownFingerprints() {
- return mUnknownFingerprints;
- }
-
- @Override
- public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- handleEnumeratedFingerprint(identifier, remaining);
- if (remaining == 0) {
- doFingerprintCleanup();
- }
- return remaining == 0;
- }
-
- @Override
- protected int statsModality() {
- return FingerprintService.this.statsModality();
- }
- }
-
- /**
- * An internal class to help clean up unknown fingerprints in hardware and software.
- */
- private final class InternalRemovalClient extends BiometricServiceBase.RemovalClientImpl {
- public InternalRemovalClient(Context context,
- DaemonWrapper daemon, long halDeviceId, IBinder token,
- ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted,
- String owner) {
- super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId,
- restricted,
- owner);
- }
-
- @Override
- protected int statsModality() {
- return FingerprintService.this.statsModality();
- }
- }
-
private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
new CopyOnWriteArrayList<>();
@GuardedBy("this")
private IBiometricsFingerprint mDaemon;
-
- private long mHalDeviceId;
- private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
- private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
+ private final SparseBooleanArray mTimedLockoutCleared;
+ private final SparseIntArray mFailedAttempts;
+ private final AlarmManager mAlarmManager;
+ private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
+ protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
+ new ResetFailedAttemptsForUserRunnable();
/**
* Receives callbacks from the HAL.
@@ -646,13 +599,7 @@
@Override
public void onError(final long deviceId, final int error, final int vendorCode) {
mHandler.post(() -> {
- ClientMonitor client = getCurrentClient();
- if (client instanceof InternalRemovalClient
- || client instanceof InternalEnumerateClient) {
- clearEnumerateState();
- }
FingerprintService.super.handleError(deviceId, error, vendorCode);
-
// TODO: this chunk of code should be common to all biometric services
if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
// If we get HW_UNAVAILABLE, try to connect again later...
@@ -673,11 +620,6 @@
ClientMonitor client = getCurrentClient();
final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
FingerprintService.super.handleRemoved(fp, remaining);
- if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
- cleanupUnknownFingerprints();
- } else if (client instanceof InternalRemovalClient){
- clearEnumerateState();
- }
});
}
@@ -685,9 +627,8 @@
public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
final int remaining) {
mHandler.post(() -> {
- // TODO: factor out common enumerate logic if possible
final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
- FingerprintService.this.handleEnumerate(fp, remaining);
+ FingerprintService.super.handleEnumerate(fp, remaining);
});
}
@@ -748,10 +689,22 @@
}
return daemon.enroll(cryptoToken, groupId, timeout);
}
+
+ @Override
+ public void resetLockout(byte[] token) throws RemoteException {
+ // TODO: confirm security token when we move timeout management into the HAL layer.
+ Slog.e(TAG, "Not supported");
+ return;
+ }
};
public FingerprintService(Context context) {
super(context);
+ mTimedLockoutCleared = new SparseBooleanArray();
+ mFailedAttempts = new SparseIntArray();
+ mAlarmManager = context.getSystemService(AlarmManager.class);
+ context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
+ getLockoutBroadcastPermission(), null /* handler */);
}
@Override
@@ -767,21 +720,16 @@
}
@Override
+ protected DaemonWrapper getDaemonWrapper() {
+ return mDaemonWrapper;
+ }
+
+ @Override
protected BiometricUtils getBiometricUtils() {
return FingerprintUtils.getInstance();
}
@Override
- protected int getFailedAttemptsLockoutTimed() {
- return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
- }
-
- @Override
- protected int getFailedAttemptsLockoutPermanent() {
- return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
- }
-
- @Override
protected Metrics getMetrics() {
return mFingerprintMetrics;
}
@@ -790,7 +738,7 @@
protected boolean hasReachedEnrollmentLimit(int userId) {
final int limit = getContext().getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
- final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
+ final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size();
if (enrolled >= limit) {
Slog.w(TAG, "Too many fingerprints registered");
return true;
@@ -866,19 +814,6 @@
}
@Override
- protected void handleUserSwitching(int userId) {
- if (getCurrentClient() instanceof InternalRemovalClient
- || getCurrentClient() instanceof InternalEnumerateClient) {
- Slog.w(TAG, "User switched while performing cleanup");
- removeClient(getCurrentClient());
- clearEnumerateState();
- }
- updateActiveGroup(userId, null);
- doFingerprintCleanupForUser(userId);
- }
-
-
- @Override
protected boolean hasEnrolledBiometrics(int userId) {
if (userId != UserHandle.getCallingUserId()) {
checkPermission(INTERACT_ACROSS_USERS);
@@ -913,6 +848,11 @@
}
@Override
+ protected List<Fingerprint> getEnrolledTemplates(int userId) {
+ return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ }
+
+ @Override
protected void notifyClientActiveCallbacks(boolean isActive) {
List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
for (int i = 0; i < callbacks.size(); i++) {
@@ -930,6 +870,20 @@
return BiometricsProtoEnums.MODALITY_FINGERPRINT;
}
+ @Override
+ protected int getLockoutMode() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+ if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
+ return AuthenticationClient.LOCKOUT_PERMANENT;
+ } else if (failedAttempts > 0
+ && !mTimedLockoutCleared.get(currentUser, false)
+ && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
+ return AuthenticationClient.LOCKOUT_TIMED;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+
/** Gets the fingerprint daemon */
private synchronized IBiometricsFingerprint getFingerprintDaemon() {
if (mDaemon == null) {
@@ -959,7 +913,7 @@
if (mHalDeviceId != 0) {
loadAuthenticatorIds();
updateActiveGroup(ActivityManager.getCurrentUser(), null);
- doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
+ doTemplateCleanupForUser(ActivityManager.getCurrentUser());
} else {
Slog.w(TAG, "Failed to open Fingerprint HAL!");
MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1);
@@ -969,79 +923,6 @@
return mDaemon;
}
- /**
- * This method should be called upon connection to the daemon, and when user switches.
- * @param userId
- */
- private void doFingerprintCleanupForUser(int userId) {
- if (CLEANUP_UNUSED_FP) {
- enumerateUser(userId);
- }
- }
-
- private void clearEnumerateState() {
- if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
- mUnknownFingerprints.clear();
- }
-
- private void enumerateUser(int userId) {
- if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
-
- final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
-
- InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper,
- mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted,
- getContext().getOpPackageName(), enrolledList);
- enumerateInternal(client);
- }
-
- // Remove unknown fingerprints from hardware
- private void cleanupUnknownFingerprints() {
- if (!mUnknownFingerprints.isEmpty()) {
- UserFingerprint uf = mUnknownFingerprints.get(0);
- mUnknownFingerprints.remove(uf);
- boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper,
- mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(),
- uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName());
- removeInternal(client);
- StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(),
- BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
- } else {
- clearEnumerateState();
- }
- }
-
- private void handleEnumerate(Fingerprint fingerprint, int remaining) {
- ClientMonitor client = getCurrentClient();
-
- if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
- return;
- }
- client.onEnumerationResult(fingerprint, remaining);
-
- // All fingerprints in hardware for this user were enumerated
- if (remaining == 0) {
- if (client instanceof InternalEnumerateClient) {
- List<Fingerprint> unknownFingerprints =
- ((InternalEnumerateClient) client).getUnknownFingerprints();
-
- if (!unknownFingerprints.isEmpty()) {
- Slog.w(TAG, "Adding " + unknownFingerprints.size() +
- " fingerprints for deletion");
- }
- for (Fingerprint f : unknownFingerprints) {
- mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
- }
- removeClient(client);
- cleanupUnknownFingerprints();
- } else {
- removeClient(client);
- }
- }
- }
-
private long startPreEnroll(IBinder token) {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
@@ -1070,8 +951,38 @@
return 0;
}
- private List<Fingerprint> getEnrolledFingerprints(int userId) {
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ // Attempt counter should only be cleared when Keyguard goes away or when
+ // a biometric is successfully authenticated. Lockout should eventually be done below the HAL.
+ // See AuthenticationClient#shouldFrameworkHandleLockout().
+ private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+ if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
+ }
+ if (clearAttemptCounter) {
+ mFailedAttempts.put(userId, 0);
+ }
+ mTimedLockoutCleared.put(userId, true);
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+ // the alarm might still be pending; remove it.
+ cancelLockoutResetForUser(userId);
+ notifyLockoutResetMonitors();
+ }
+
+
+ private void cancelLockoutResetForUser(int userId) {
+ mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+ }
+
+ private void scheduleLockoutResetForUser(int userId) {
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ getLockoutResetIntentForUser(userId));
+ }
+
+ private PendingIntent getLockoutResetIntentForUser(int userId) {
+ return PendingIntent.getBroadcast(getContext(), userId,
+ new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT);
}
private void dumpInternal(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index eb457b6..cb8a772 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -17,12 +17,16 @@
package com.android.server.biometrics.iris;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
+import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.Metrics;
+import java.util.List;
+
/**
* A service to manage multiple clients that want to access the Iris HAL API.
* The service is responsible for maintaining a list of clients and dispatching all
@@ -61,18 +65,13 @@
}
@Override
- protected BiometricUtils getBiometricUtils() {
+ protected DaemonWrapper getDaemonWrapper() {
return null;
}
@Override
- protected int getFailedAttemptsLockoutTimed() {
- return 0;
- }
-
- @Override
- protected int getFailedAttemptsLockoutPermanent() {
- return 0;
+ protected BiometricUtils getBiometricUtils() {
+ return null;
}
@Override
@@ -106,11 +105,6 @@
}
@Override
- protected void handleUserSwitching(int userId) {
-
- }
-
- @Override
protected boolean hasEnrolledBiometrics(int userId) {
return false;
}
@@ -131,7 +125,17 @@
}
@Override
+ protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) {
+ return null;
+ }
+
+ @Override
protected int statsModality() {
return BiometricsProtoEnums.MODALITY_IRIS;
}
+
+ @Override
+ protected int getLockoutMode() {
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 19bdc09..c91e1a1 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1859,7 +1859,7 @@
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
- mDeps.getIpServerDependencies(mContext)));
+ mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e1af81b..da547ea 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -241,7 +241,7 @@
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
- updateCapabilities();
+ updateCapabilities(null /* defaultNetwork */);
loadAlwaysOnPackage();
}
@@ -268,22 +268,44 @@
updateAlwaysOnNotification(detailedState);
}
- public void updateCapabilities() {
- final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null;
- // Only apps targeting Q and above can explicitly declare themselves as metered.
- final boolean isAlwaysMetered =
- mIsPackageTargetingAtLeastQ && (mConfig == null || mConfig.isMetered);
- updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks,
- mNetworkCapabilities, isAlwaysMetered);
-
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ /**
+ * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a
+ * defensive copy.
+ *
+ * <p>Does not propagate updated capabilities to apps.
+ *
+ * @param defaultNetwork underlying network for VPNs following platform's default
+ */
+ public synchronized NetworkCapabilities updateCapabilities(
+ @Nullable Network defaultNetwork) {
+ if (mConfig == null) {
+ // VPN is not running.
+ return null;
}
+
+ Network[] underlyingNetworks = mConfig.underlyingNetworks;
+ if (underlyingNetworks == null && defaultNetwork != null) {
+ // null underlying networks means to track the default.
+ underlyingNetworks = new Network[] { defaultNetwork };
+ }
+ // Only apps targeting Q and above can explicitly declare themselves as metered.
+ final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;
+
+ applyUnderlyingCapabilities(
+ mContext.getSystemService(ConnectivityManager.class),
+ underlyingNetworks,
+ mNetworkCapabilities,
+ isAlwaysMetered);
+
+ return new NetworkCapabilities(mNetworkCapabilities);
}
@VisibleForTesting
- public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks,
- NetworkCapabilities caps, boolean isAlwaysMetered) {
+ public static void applyUnderlyingCapabilities(
+ ConnectivityManager cm,
+ Network[] underlyingNetworks,
+ NetworkCapabilities caps,
+ boolean isAlwaysMetered) {
int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -296,6 +318,7 @@
boolean hadUnderlyingNetworks = false;
if (null != underlyingNetworks) {
for (Network underlying : underlyingNetworks) {
+ // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
if (underlyingCaps == null) continue;
hadUnderlyingNetworks = true;
@@ -1007,9 +1030,8 @@
}
/**
- * Establish a VPN network and return the file descriptor of the VPN
- * interface. This methods returns {@code null} if the application is
- * revoked or not prepared.
+ * Establish a VPN network and return the file descriptor of the VPN interface. This methods
+ * returns {@code null} if the application is revoked or not prepared.
*
* @param config The parameters to configure the network.
* @return The file descriptor of the VPN interface.
@@ -1101,8 +1123,6 @@
// as rules are deleted. This prevents data leakage as the rules are moved over.
agentDisconnect(oldNetworkAgent);
}
- // Set up VPN's capabilities such as meteredness.
- updateCapabilities();
if (oldConnection != null) {
mContext.unbindService(oldConnection);
@@ -1258,6 +1278,11 @@
return ranges;
}
+ /**
+ * Updates UID ranges for this VPN and also updates its internal capabilities.
+ *
+ * <p>Should be called on primary ConnectivityService thread.
+ */
public void onUserAdded(int userHandle) {
// If the user is restricted tie them to the parent user's VPN
UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1268,8 +1293,9 @@
try {
addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
mConfig.disallowedApplications);
+ // ConnectivityService will call {@link #updateCapabilities} and apply
+ // those for VPN network.
mNetworkCapabilities.setUids(existingRanges);
- updateCapabilities();
} catch (Exception e) {
Log.wtf(TAG, "Failed to add restricted user to owner", e);
}
@@ -1279,6 +1305,11 @@
}
}
+ /**
+ * Updates UID ranges for this VPN and also updates its capabilities.
+ *
+ * <p>Should be called on primary ConnectivityService thread.
+ */
public void onUserRemoved(int userHandle) {
// clean up if restricted
UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1290,8 +1321,9 @@
final List<UidRange> removedRanges =
uidRangesForUser(userHandle, existingRanges);
existingRanges.removeAll(removedRanges);
+ // ConnectivityService will call {@link #updateCapabilities} and
+ // apply those for VPN network.
mNetworkCapabilities.setUids(existingRanges);
- updateCapabilities();
} catch (Exception e) {
Log.wtf(TAG, "Failed to remove restricted user to owner", e);
}
@@ -1504,6 +1536,12 @@
return success;
}
+ /**
+ * Updates underlying network set.
+ *
+ * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from
+ * ConnectivityService thread to get updated capabilities.
+ */
public synchronized boolean setUnderlyingNetworks(Network[] networks) {
if (!isCallerEstablishedOwnerLocked()) {
return false;
@@ -1520,7 +1558,6 @@
}
}
}
- updateCapabilities();
return true;
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 3fddac1..173d786 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -61,8 +61,8 @@
/**
* Get dependencies to be used by IpServer.
*/
- public IpServer.Dependencies getIpServerDependencies(Context context) {
- return new IpServer.Dependencies(context);
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies();
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dc5be6a..15c7ef7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1019,7 +1019,7 @@
if (mDisplayWhiteBalanceController != null) {
if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
mDisplayWhiteBalanceController.setEnabled(true);
- mDisplayWhiteBalanceController.updateScreenColorTemperature();
+ mDisplayWhiteBalanceController.updateDisplayColorTemperature();
} else {
mDisplayWhiteBalanceController.setEnabled(false);
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index b9aa34e8..d95e92b 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -29,14 +29,14 @@
/**
* The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
- * screen color temperature depending on the ambient color temperature).
+ * display color temperature depending on the ambient color temperature).
*
* The DisplayWhiteBalanceController:
* - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
* - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
* noise, and arrive at an estimate of the actual ambient color temperature;
- * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be
- * updated, suppressing changes that are too frequent or too minor.
+ * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should
+ * be updated, suppressing changes that are too frequent or too minor.
*/
public class DisplayWhiteBalanceController implements
AmbientSensor.AmbientBrightnessSensor.Callbacks,
@@ -76,8 +76,8 @@
// Override the ambient color temperature for debugging purposes.
private float mAmbientColorTemperatureOverride;
- // A piecewise linear relationship between ambient and display color temperatures
- private Spline.LinearSpline mAmbientToDisplayTemperatureSpline;
+ // A piecewise linear relationship between ambient and display color temperatures.
+ private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
/**
* @param brightnessSensor
@@ -91,7 +91,7 @@
* The filter used to average ambient color temperature changes over time, filter out the
* noise and arrive at an estimate of the actual ambient color temperature.
* @param throttler
- * The throttler used to determine whether the new screen color temperature should be
+ * The throttler used to determine whether the new display color temperature should be
* updated or not.
* @param lowLightAmbientBrightnessThreshold
* The ambient brightness threshold beneath which we fall back to a fixed ambient color
@@ -99,6 +99,12 @@
* @param lowLightAmbientColorTemperature
* The ambient color temperature to which we fall back when the ambient brightness drops
* beneath a certain threshold.
+ * @param ambientColorTemperatures
+ * The ambient color tempeartures used to map the ambient color temperature to the display
+ * color temperature (or null if no mapping is necessary).
+ * @param displayColorTemperatures
+ * The display color temperatures used to map the ambient color temperature to the display
+ * color temperature (or null if no mapping is necessary).
*
* @throws NullPointerException
* - brightnessSensor is null;
@@ -114,7 +120,7 @@
@NonNull AmbientFilter colorTemperatureFilter,
@NonNull DisplayWhiteBalanceThrottler throttler,
float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature,
- float[] ambientTemperatures, float[] displayTemperatures) {
+ float[] ambientColorTemperatures, float[] displayColorTemperatures) {
validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
colorTemperatureFilter, throttler);
mLoggingEnabled = false;
@@ -134,10 +140,10 @@
mAmbientColorTemperatureOverride = -1.0f;
try {
- mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures,
- displayTemperatures);
+ mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
+ ambientColorTemperatures, displayColorTemperatures);
} catch (Exception e) {
- mAmbientToDisplayTemperatureSpline = null;
+ mAmbientToDisplayColorTemperatureSpline = null;
}
mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
@@ -160,7 +166,7 @@
}
/**
- * Set an object to call back to when the screen color temperature should be updated.
+ * Set an object to call back to when the display color temperature should be updated.
*
* @param callbacks
* The object to call back to.
@@ -201,7 +207,7 @@
*
* This is only applied when the ambient color temperature changes or is updated (in which case
* it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
- * change the screen color temperature immediately.
+ * change the display color temperature immediately.
*
* @param ambientColorTemperatureOverride
* The ambient color temperature override.
@@ -240,9 +246,8 @@
writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
writer.println(" mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
- writer.println(" mAmbientToDisplayTemperatureSpline="
- + (mAmbientToDisplayTemperatureSpline == null ? "unused" :
- mAmbientToDisplayTemperatureSpline));
+ writer.println(" mAmbientToDisplayColorTemperatureSpline="
+ + mAmbientToDisplayColorTemperatureSpline);
}
@Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -266,9 +271,9 @@
final long time = System.currentTimeMillis();
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
- if (mAmbientToDisplayTemperatureSpline != null) {
+ if (mAmbientToDisplayColorTemperatureSpline != null) {
ambientColorTemperature =
- mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature);
+ mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
}
final float ambientBrightness = mBrightnessFilter.getEstimate(time);
@@ -290,7 +295,7 @@
ambientColorTemperature = mAmbientColorTemperatureOverride;
}
- // When the screen color temperature needs to be updated, we call DisplayPowerController to
+ // When the display color temperature needs to be updated, we call DisplayPowerController to
// call our updateColorTemperature. The reason we don't call it directly is that we want
// all changes to the system to happen in a predictable order in DPC's main loop
// (updatePowerState).
@@ -308,9 +313,9 @@
}
/**
- * Updates the screen color temperature.
+ * Updates the display color temperature.
*/
- public void updateScreenColorTemperature() {
+ public void updateDisplayColorTemperature() {
float ambientColorTemperature = -1.0f;
// If both the pending and the current ambient color temperatures are -1, it means the DWBC
@@ -353,7 +358,7 @@
* Called whenever the display white-balance state has changed.
*
* Usually, this means the estimated ambient color temperature has changed enough, and the
- * screen color temperature should be updated; but it is also called by
+ * display color temperature should be updated; but it is also called if settings change.
*/
void updateWhiteBalance();
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index 56f4ca3..449f115 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -67,14 +67,14 @@
final float lowLightAmbientColorTemperature = getFloat(resources,
com.android.internal.R.dimen
.config_displayWhiteBalanceLowLightAmbientColorTemperature);
- final float[] ambientTemperatures = getFloatArray(resources,
- com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues);
- final float[] displayTemperatures = getFloatArray(resources,
- com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues);
+ final float[] ambientColorTemperatures = getFloatArray(resources,
+ com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
+ final float[] displayColorTempeartures = getFloatArray(resources,
+ com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures);
final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature,
- ambientTemperatures, displayTemperatures);
+ ambientColorTemperatures, displayColorTempeartures);
brightnessSensor.setCallbacks(controller);
colorTemperatureSensor.setCallbacks(controller);
return controller;
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
index c1f0e98..5941bbb 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
@@ -23,7 +23,7 @@
/**
* The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the
- * screen color temperature should be updated, suppressing changes that are too frequent or too
+ * display color temperature should be updated, suppressing changes that are too frequent or too
* minor.
*/
class DisplayWhiteBalanceThrottler {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a9ae74f..4b4788c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -55,6 +55,8 @@
import android.database.ContentObserver;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.face.FaceManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -671,7 +673,6 @@
mDeviceProvisionedObserver.onSystemReady();
// TODO: maybe skip this for split system user mode.
mStorage.prefetchUser(UserHandle.USER_SYSTEM);
- mStrongAuth.systemReady();
}
private void migrateOldData() {
@@ -2375,6 +2376,14 @@
userCredential = null;
}
+ final PackageManager pm = mContext.getPackageManager();
+ // TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
+ // we need to generate challenge for each one, have it signed by GK and reset lockout
+ // for each modality.
+ if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
+ }
+
final AuthenticationResult authResult;
VerifyCredentialResponse response;
synchronized (mSpManager) {
@@ -2413,6 +2422,17 @@
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
notifyActivePasswordMetricsAvailable(userCredential, userId);
unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
+ // Reset lockout
+ if (BiometricManager.hasBiometrics(mContext)) {
+ BiometricManager bm = mContext.getSystemService(BiometricManager.class);
+ Slog.i(TAG, "Resetting lockout, length: "
+ + authResult.gkResponse.getPayload().length);
+ bm.resetLockout(authResult.gkResponse.getPayload());
+
+ if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ mContext.getSystemService(FaceManager.class).revokeChallenge();
+ }
+ }
final byte[] secret = authResult.authToken.deriveDiskEncryptionKey();
Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + secret.length);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 4480435..a84306c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -16,15 +16,16 @@
package com.android.server.locksettings;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.content.Context;
-import android.hardware.biometrics.BiometricManager;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteCallbackList;
@@ -61,7 +62,6 @@
private final Context mContext;
private AlarmManager mAlarmManager;
- private BiometricManager mBiometricManager;
public LockSettingsStrongAuth(Context context) {
mContext = context;
@@ -69,12 +69,6 @@
mAlarmManager = context.getSystemService(AlarmManager.class);
}
- public void systemReady() {
- if (BiometricManager.hasBiometrics(mContext)) {
- mBiometricManager = mContext.getSystemService(BiometricManager.class);
- }
- }
-
private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
mTrackers.register(tracker);
@@ -185,11 +179,6 @@
}
public void reportSuccessfulStrongAuthUnlock(int userId) {
- if (mBiometricManager != null) {
- byte[] token = null; /* TODO: pass real auth token once HAL supports it */
- mBiometricManager.resetTimeout(token);
- }
-
final int argNotUsed = 0;
mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 941de89..61a1a2f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -985,6 +985,9 @@
@GuardedBy("mPackages")
private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider;
+ @GuardedBy("mPackages")
+ private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider;
+
private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
private Context mContext;
private ComponentName mIntentFilterVerifierComponent;
@@ -1349,7 +1352,7 @@
final @Nullable String mRequiredVerifierPackage;
final @NonNull String mRequiredInstallerPackage;
final @NonNull String mRequiredUninstallerPackage;
- final String mRequiredPermissionControllerPackage;
+ final @NonNull String mRequiredPermissionControllerPackage;
final @Nullable String mSetupWizardPackage;
final @Nullable String mStorageManagerPackage;
final @Nullable String mSystemTextClassifierPackage;
@@ -1940,6 +1943,10 @@
// We may also need to apply pending (restored) runtime
// permission grants within these users.
mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
+
+ // Persistent preferred activity might have came into effect due to this
+ // install.
+ updateDefaultHomeLPw(userId);
}
}
}
@@ -19077,6 +19084,7 @@
pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
scheduleWritePackageRestrictionsLocked(userId);
postPreferredActivityChangedBroadcast(userId);
+ updateDefaultHomeLPw(userId);
}
}
@@ -19227,6 +19235,13 @@
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mPackages")
boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) {
+ return clearPackagePreferredActivitiesLPw(packageName, false, userId);
+ }
+
+ /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+ @GuardedBy("mPackages")
+ private boolean clearPackagePreferredActivitiesLPw(String packageName,
+ boolean skipUpdateDefaultHome, int userId) {
ArrayList<PreferredActivity> removed = null;
boolean changed = false;
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
@@ -19255,6 +19270,9 @@
pir.removeFilter(pa);
}
changed = true;
+ if (!skipUpdateDefaultHome) {
+ updateDefaultHomeLPw(thisUserId);
+ }
}
}
if (changed) {
@@ -19314,8 +19332,9 @@
// writer
try {
synchronized (mPackages) {
- clearPackagePreferredActivitiesLPw(null, userId);
+ clearPackagePreferredActivitiesLPw(null, true, userId);
mSettings.applyDefaultPreferredAppsLPw(userId);
+ updateDefaultHomeLPw(userId);
// TODO: We have to reset the default SMS and Phone. This requires
// significant refactoring to keep all default apps in the package
// manager (cleaner but more work) or have the services provide
@@ -19384,6 +19403,7 @@
new PersistentPreferredActivity(filter, activity));
scheduleWritePackageRestrictionsLocked(userId);
postPreferredActivityChangedBroadcast(userId);
+ updateDefaultHomeLPw(userId);
}
}
@@ -19427,6 +19447,7 @@
if (changed) {
scheduleWritePackageRestrictionsLocked(userId);
postPreferredActivityChangedBroadcast(userId);
+ updateDefaultHomeLPw(userId);
}
}
}
@@ -19514,6 +19535,7 @@
(readParser, readUserId) -> {
synchronized (mPackages) {
mSettings.readPreferredActivitiesLPw(readParser, readUserId);
+ updateDefaultHomeLPw(readUserId);
}
});
} catch (Exception e) {
@@ -19569,8 +19591,17 @@
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
(parser1, userId1) -> {
+ String defaultBrowser;
synchronized (mPackages) {
mSettings.readDefaultAppsLPw(parser1, userId1);
+ defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
+ }
+ if (defaultBrowser != null) {
+ PackageManagerInternal.DefaultBrowserProvider provider;
+ synchronized (mPackages) {
+ provider = mDefaultBrowserProvider;
+ }
+ provider.setDefaultBrowser(defaultBrowser, userId1);
}
});
} catch (Exception e) {
@@ -19928,19 +19959,59 @@
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
Intent intent = getHomeIntent();
- List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null,
+ List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
PackageManager.GET_META_DATA, userId);
- ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0,
- true, false, false, userId);
-
allHomeCandidates.clear();
- if (list != null) {
- allHomeCandidates.addAll(list);
+ if (resolveInfos == null) {
+ return null;
}
- return (preferred == null || preferred.activityInfo == null)
- ? null
- : new ComponentName(preferred.activityInfo.packageName,
- preferred.activityInfo.name);
+ allHomeCandidates.addAll(resolveInfos);
+
+ PackageManagerInternal.DefaultHomeProvider provider;
+ synchronized (mPackages) {
+ provider = mDefaultHomeProvider;
+ }
+ if (provider == null) {
+ Slog.e(TAG, "mDefaultHomeProvider is null");
+ return null;
+ }
+ String packageName = provider.getDefaultHome(userId);
+ if (packageName == null) {
+ return null;
+ }
+ int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+
+ if (resolveInfo.activityInfo != null && TextUtils.equals(
+ resolveInfo.activityInfo.packageName, packageName)) {
+ return new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ }
+ }
+ return null;
+ }
+
+ private void updateDefaultHomeLPw(int userId) {
+ Intent intent = getHomeIntent();
+ List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
+ PackageManager.GET_META_DATA, userId);
+ ResolveInfo preferredResolveInfo = findPreferredActivity(intent, null, 0, resolveInfos,
+ 0, true, false, false, userId);
+ String packageName = preferredResolveInfo != null
+ && preferredResolveInfo.activityInfo != null
+ ? preferredResolveInfo.activityInfo.packageName : null;
+ String currentPackageName = mDefaultHomeProvider.getDefaultHome(userId);
+ if (TextUtils.equals(currentPackageName, packageName)) {
+ return;
+ }
+ String[] callingPackages = getPackagesForUid(Binder.getCallingUid());
+ if (callingPackages != null && ArrayUtils.contains(callingPackages,
+ mRequiredPermissionControllerPackage)) {
+ // PermissionController manages default home directly.
+ return;
+ }
+ mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId);
}
@Override
@@ -23830,6 +23901,13 @@
mDefaultBrowserProvider = provider;
}
}
+
+ @Override
+ public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) {
+ synchronized (mPackages) {
+ mDefaultHomeProvider = provider;
+ }
+ }
}
@GuardedBy("mPackages")
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index 3534cf3..888dd99 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -17,10 +17,13 @@
package com.android.server.policy.role;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
import android.os.Debug;
import android.provider.Settings;
import android.telecom.TelecomManager;
@@ -33,6 +36,7 @@
import com.android.server.LocalServices;
import com.android.server.role.RoleManagerService;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -54,19 +58,44 @@
@NonNull
private final Context mContext;
- public LegacyRoleResolutionPolicy(Context context) {
+ public LegacyRoleResolutionPolicy(@NonNull Context context) {
mContext = context;
}
+ @NonNull
@Override
- public List<String> getRoleHolders(String roleName, int userId) {
+ public List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId) {
switch (roleName) {
+ case RoleManager.ROLE_ASSISTANT: {
+ String legacyAssistant = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
+ if (legacyAssistant == null || legacyAssistant.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singletonList(
+ ComponentName.unflattenFromString(legacyAssistant).getPackageName());
+ }
+ }
+ case RoleManager.ROLE_BROWSER: {
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
+ userId);
+ return CollectionUtils.singletonOrEmpty(packageName);
+ }
+ case RoleManager.ROLE_DIALER: {
+ String setting = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
+ return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting)
+ ? setting
+ : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
+ }
case RoleManager.ROLE_SMS: {
// Moved over from SmsApplication#getApplication
String result = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
-
// TODO: STOPSHIP: Remove the following code once we read the value of
// config_defaultSms in RoleControllerService.
if (result == null) {
@@ -92,34 +121,13 @@
SmsApplication.SmsApplicationData app = applicationData;
result = app == null ? null : app.mPackageName;
}
-
return CollectionUtils.singletonOrEmpty(result);
}
- case RoleManager.ROLE_ASSISTANT: {
- String legacyAssistant = Settings.Secure.getStringForUser(
- mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
-
- if (legacyAssistant == null || legacyAssistant.isEmpty()) {
- return Collections.emptyList();
- } else {
- return Collections.singletonList(
- ComponentName.unflattenFromString(legacyAssistant).getPackageName());
- }
- }
- case RoleManager.ROLE_DIALER: {
- String setting = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
-
- return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting)
- ? setting
- : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
- }
- case RoleManager.ROLE_BROWSER: {
- PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
- String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
- userId);
+ case RoleManager.ROLE_HOME: {
+ PackageManager packageManager = mContext.getPackageManager();
+ List<ResolveInfo> resolveInfos = new ArrayList<>();
+ ComponentName componentName = packageManager.getHomeActivities(resolveInfos);
+ String packageName = componentName != null ? componentName.getPackageName() : null;
return CollectionUtils.singletonOrEmpty(packageName);
}
default: {
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 4186154..8740256 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -123,6 +123,9 @@
public AttentionDetector(Runnable onUserAttention, Object lock) {
mOnUserAttention = onUserAttention;
mLock = lock;
+
+ // Device starts with an awake state upon boot.
+ mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
}
public void systemReady(Context context) {
@@ -145,7 +148,7 @@
if (DEBUG) {
Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
}
- return nextScreenDimming;
+ return whenToCheck;
} else if (whenToStopExtending < whenToCheck) {
if (DEBUG) {
Slog.d(TAG, "Let device sleep to avoid false results and improve security "
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 21bf9de..d853121 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -111,7 +111,8 @@
/** @see #getRoleHolders(String, int) */
public interface RoleHoldersResolver {
/** @return a list of packages that hold a given role for a given user */
- List<String> getRoleHolders(String roleName, int userId);
+ @NonNull
+ List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId);
}
/**
@@ -154,6 +155,7 @@
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider());
+ packageManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider());
registerUserRemovedReceiver();
}
@@ -741,4 +743,33 @@
}
}
}
+
+ private class DefaultHomeProvider implements PackageManagerInternal.DefaultHomeProvider {
+
+ @Nullable
+ @Override
+ public String getDefaultHome(@UserIdInt int userId) {
+ return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders(
+ RoleManager.ROLE_HOME));
+ }
+
+ @Override
+ public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId) {
+ IRoleManagerCallback callback = new IRoleManagerCallback.Stub() {
+ @Override
+ public void onSuccess() {}
+ @Override
+ public void onFailure() {
+ Slog.e(LOG_TAG, "Failed to set default home: " + packageName);
+ }
+ };
+ if (packageName != null) {
+ getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME,
+ packageName, 0, callback);
+ } else {
+ getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0,
+ callback);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f3393e2..edca2e3 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1942,6 +1942,20 @@
}
}
+ private void pullTimeZoneDataInfo(int tagId,
+ long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ String tzDbVersion = "Unknown";
+ try {
+ tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
+ } catch (Exception e) {
+ Log.e(TAG, "Getting tzdb version failed: ", e);
+ }
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(tzDbVersion);
+ pulledData.add(e);
+ }
+
/**
* Pulls various data.
*/
@@ -2130,6 +2144,10 @@
pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.TIME_ZONE_DATA_INFO: {
+ pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index ff01d46..b121298 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -19,6 +19,7 @@
import static android.app.StatusBarManager.DISABLE2_NONE;
import static android.app.StatusBarManager.DISABLE_NONE;
+import android.app.StatusBarManager.DisableInfo;
import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
@@ -26,6 +27,7 @@
import android.os.RemoteException;
import android.os.ShellCommand;
import android.service.quicksettings.TileService;
+import android.util.Pair;
import java.io.PrintWriter;
@@ -68,6 +70,8 @@
return runGetStatusIcons();
case "disable-for-setup":
return runDisableForSetup();
+ case "send-disable-flag":
+ return runSendDisableFlag();
default:
return handleDefaultCommands(cmd);
}
@@ -132,6 +136,47 @@
return 0;
}
+ private int runSendDisableFlag() {
+ String pkg = mContext.getPackageName();
+ int disable1 = DISABLE_NONE;
+ int disable2 = DISABLE2_NONE;
+
+ DisableInfo info = new DisableInfo();
+
+ String arg = getNextArg();
+ while (arg != null) {
+ switch (arg) {
+ case "search":
+ info.setSearchDisabled(true);
+ break;
+ case "home":
+ info.setNagivationHomeDisabled(true);
+ break;
+ case "recents":
+ info.setRecentsDisabled(true);
+ break;
+ case "notification-alerts":
+ info.setNotificationPeekingDisabled(true);
+ break;
+ case "statusbar-expansion":
+ info.setStatusBarExpansionDisabled(true);
+ break;
+
+ default:
+ break;
+ }
+
+ arg = getNextArg();
+ }
+
+ Pair<Integer, Integer> flagPair = info.toFlags();
+
+ mInterface.disable(flagPair.first, sToken, pkg);
+ mInterface.disable2(flagPair.second, sToken, pkg);
+
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -166,6 +211,17 @@
pw.println(" disable-for-setup DISABLE");
pw.println(" If true, disable status bar components unsuitable for device setup");
pw.println("");
+ pw.println(" send-disable-flag FLAG...");
+ pw.println(" Send zero or more disable flags (parsed individually) to StatusBarManager");
+ pw.println(" Valid options:");
+ pw.println(" <blank> - equivalent to \"none\"");
+ pw.println(" none - re-enables all components");
+ pw.println(" search - disable search");
+ pw.println(" home - disable naviagation home");
+ pw.println(" recents - disable recents/overview");
+ pw.println(" notification-peek - disable notification peeking");
+ pw.println(" statusbar-expansion - disable status bar expansion");
+ pw.println("");
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c33a2c1..d40948b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -830,12 +830,25 @@
new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT, null);
- final int flags = intent.getFlags();
Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- newIntent.setFlags(flags
- | FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+ int flags = intent.getFlags();
+ flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
+ /*
+ * Prevent reuse of review activity: Each app needs their own review activity. By
+ * default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities
+ * with the same launch parameters (extras are ignored). Hence to avoid possible
+ * reuse force a new activity via the MULTIPLE_TASK flag.
+ *
+ * Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used,
+ * hence no need to add the flag in this case.
+ */
+ if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) {
+ flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+ }
+ newIntent.setFlags(flags);
+
newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
if (resultRecord != null) {
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 5c19ad3..9cbb58d 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "BatteryStatsService"
//#define LOG_NDEBUG 0
+#include <climits>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -28,6 +29,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <unordered_map>
+#include <utility>
#include <android/hardware/power/1.0/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
@@ -87,6 +89,15 @@
std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};
+// Cellular/Wifi power monitor rail information
+static jmethodID jupdateRailData = NULL;
+static jmethodID jsetRailStatsAvailability = NULL;
+
+std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {};
+
+std::unordered_map<uint32_t, std::pair<std::string, std::string>> gPowerStatsHalRailNames = {};
+static bool power_monitor_available = false;
+
// The caller must be holding gPowerHalMutex.
static void deinitPowerStatsLocked() {
gPowerStatsHalV1_0 = nullptr;
@@ -258,6 +269,7 @@
gPowerStatsHalStateNames.clear();
gPowerStatsHalPlatformIds.clear();
gPowerStatsHalSubsystemIds.clear();
+ gPowerStatsHalRailNames.clear();
Return<void> ret;
ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
@@ -301,6 +313,27 @@
return false;
}
+ // Get Power monitor rails available
+ ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGW("Rail information is not available");
+ power_monitor_available = false;
+ return;
+ }
+
+ // Fill out rail names/subsystems into gPowerStatsHalRailNames
+ for (auto rail : rails) {
+ gPowerStatsHalRailNames.emplace(rail.index,
+ std::make_pair(rail.railName, rail.subsysName));
+ }
+ if (!gPowerStatsHalRailNames.empty()) {
+ power_monitor_available = true;
+ }
+ });
+ if (!checkResultLocked(ret, __func__)) {
+ return false;
+ }
+
return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
}
@@ -517,6 +550,50 @@
return total_added;
}
+static void getPowerStatsHalRailEnergyData(JNIEnv* env, jobject jrailStats) {
+ using android::hardware::power::stats::V1_0::Status;
+ using android::hardware::power::stats::V1_0::EnergyData;
+
+ if (!getPowerStatsHalLocked()) {
+ ALOGE("failed to get power stats");
+ return;
+ }
+
+ if (!power_monitor_available) {
+ env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
+ ALOGW("Rail energy data is not available");
+ return;
+ }
+
+ // Get power rail energySinceBoot data
+ Return<void> ret = gPowerStatsHalV1_0->getEnergyData({},
+ [&env, &jrailStats](auto energyData, auto status) {
+ if (status == Status::NOT_SUPPORTED) {
+ ALOGW("getEnergyData is not supported");
+ return;
+ }
+
+ for (auto data : energyData) {
+ if (!(data.timestamp > LLONG_MAX || data.energy > LLONG_MAX)) {
+ env->CallVoidMethod(jrailStats,
+ jupdateRailData,
+ data.index,
+ env->NewStringUTF(
+ gPowerStatsHalRailNames.at(data.index).first.c_str()),
+ env->NewStringUTF(
+ gPowerStatsHalRailNames.at(data.index).second.c_str()),
+ data.timestamp,
+ data.energy);
+ } else {
+ ALOGE("Java long overflow seen. Rail index %d not updated", data.index);
+ }
+ }
+ });
+ if (!checkResultLocked(ret, __func__)) {
+ ALOGE("getEnergyData failed");
+ }
+}
+
// The caller must be holding powerHalMutex.
static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
@@ -761,11 +838,13 @@
gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData;
gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData;
gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData;
+ gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyData;
} else if (android::hardware::power::V1_0::IPower::getService() != nullptr) {
ALOGI("Using power HAL");
gGetLowPowerStatsImpl = getPowerHalLowPowerData;
gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
+ gGetRailEnergyPowerStatsImpl = NULL;
}
}
@@ -835,11 +914,44 @@
return -1;
}
+static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) {
+ if (jrailStats == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "The railstats jni input jobject jrailStats is null.");
+ return;
+ }
+ if (jupdateRailData == NULL) {
+ ALOGE("A railstats jni jmethodID is null.");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+ if (!gGetRailEnergyPowerStatsImpl) {
+ setUpPowerStatsLocked();
+ }
+
+ if (gGetRailEnergyPowerStatsImpl) {
+ gGetRailEnergyPowerStatsImpl(env, jrailStats);
+ return;
+ }
+
+ if (jsetRailStatsAvailability == NULL) {
+ ALOGE("setRailStatsAvailability jni jmethodID is null.");
+ return;
+ }
+ env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
+ ALOGE("Unable to load Power.Stats.HAL. Setting rail availability to false");
+ return;
+}
+
static const JNINativeMethod method_table[] = {
{ "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
{ "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
{ "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
{ "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
+ { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V",
+ (void*)getRailEnergyPowerStats },
};
int register_android_server_BatteryStatsService(JNIEnv *env)
@@ -850,8 +962,9 @@
env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
jclass clsPowerStateSubsystem =
env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
+ jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats");
if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
- || clsPowerStateSubsystem == NULL) {
+ || clsPowerStateSubsystem == NULL || clsRailStats == NULL) {
ALOGE("A rpmstats jni jclass is null.");
} else {
jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
@@ -862,6 +975,10 @@
"(Ljava/lang/String;JI)V");
jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
"(Ljava/lang/String;JI)V");
+ jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData",
+ "(JLjava/lang/String;Ljava/lang/String;JJ)V");
+ jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability",
+ "(Z)V");
}
return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6017f2..aae159c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,6 +36,7 @@
import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
+import android.net.NetworkStackClient;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -1350,9 +1351,7 @@
traceBeginAndSlog("StartNetworkStack");
try {
- final android.net.NetworkStack networkStack =
- context.getSystemService(android.net.NetworkStack.class);
- networkStack.start(context);
+ NetworkStackClient.getInstance().start(context);
} catch (Throwable e) {
reportWtf("starting Network Stack", e);
}
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
new file mode 100644
index 0000000..1eb7b98
--- /dev/null
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * Service used to communicate with the network stack, which is running in a separate module.
+ * @hide
+ */
+public class NetworkStackClient {
+ private static final String TAG = NetworkStackClient.class.getSimpleName();
+
+ private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+
+ private static NetworkStackClient sInstance;
+
+ @NonNull
+ @GuardedBy("mPendingNetStackRequests")
+ private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
+ @Nullable
+ @GuardedBy("mPendingNetStackRequests")
+ private INetworkStackConnector mConnector;
+
+ private volatile boolean mNetworkStackStartRequested = false;
+
+ private interface NetworkStackCallback {
+ void onNetworkStackConnected(INetworkStackConnector connector);
+ }
+
+ private NetworkStackClient() { }
+
+ /**
+ * Get the NetworkStackClient singleton instance.
+ */
+ public static synchronized NetworkStackClient getInstance() {
+ if (sInstance == null) {
+ sInstance = new NetworkStackClient();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a DHCP server according to the specified parameters.
+ *
+ * <p>The server will be returned asynchronously through the provided callbacks.
+ */
+ public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+ final IDhcpServerCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeDhcpServer(ifName, params, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ /**
+ * Create an IpClient on the specified interface.
+ *
+ * <p>The IpClient will be returned asynchronously through the provided callbacks.
+ */
+ public void makeIpClient(String ifName, IIpClientCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeIpClient(ifName, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ /**
+ * Create a NetworkMonitor.
+ *
+ * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+ */
+ public void makeNetworkMonitor(
+ NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeNetworkMonitor(network, name, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ private class NetworkStackConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ registerNetworkStackService(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // TODO: crash/reboot the system ?
+ Slog.wtf(TAG, "Lost network stack connector");
+ }
+ };
+
+ private void registerNetworkStackService(@NonNull IBinder service) {
+ final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
+
+ ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
+ DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+
+ final ArrayList<NetworkStackCallback> requests;
+ synchronized (mPendingNetStackRequests) {
+ requests = new ArrayList<>(mPendingNetStackRequests);
+ mPendingNetStackRequests.clear();
+ mConnector = connector;
+ }
+
+ for (NetworkStackCallback r : requests) {
+ r.onNetworkStackConnected(connector);
+ }
+ }
+
+ /**
+ * Start the network stack. Should be called only once on device startup.
+ *
+ * <p>This method will start the network stack either in the network stack process, or inside
+ * the system server on devices that do not support the network stack module. The network stack
+ * connector will then be delivered asynchronously to clients that requested it before it was
+ * started.
+ */
+ public void start(Context context) {
+ mNetworkStackStartRequested = true;
+ // Try to bind in-process if the library is available
+ IBinder connector = null;
+ try {
+ final Class service = Class.forName(
+ "com.android.server.NetworkStackService",
+ true /* initialize */,
+ context.getClassLoader());
+ connector = (IBinder) service.getMethod("makeConnector", Context.class)
+ .invoke(null, context);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+ // TODO: crash/reboot system here ?
+ return;
+ } catch (ClassNotFoundException e) {
+ // Normal behavior if stack is provided by the app: fall through
+ }
+
+ // In-process network stack. Add the service to the service manager here.
+ if (connector != null) {
+ registerNetworkStackService(connector);
+ return;
+ }
+ // Start the network stack process. The service will be added to the service manager in
+ // NetworkStackConnection.onServiceConnected().
+ final Intent intent = new Intent(INetworkStackConnector.class.getName());
+ final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
+ intent.setComponent(comp);
+
+ if (comp == null) {
+ Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+ // TODO: crash/reboot system server ?
+ return;
+ }
+ final PackageManager pm = context.getPackageManager();
+ int uid = -1;
+ try {
+ uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf("Network stack package not found", e);
+ // Fall through
+ }
+ if (uid != Process.NETWORK_STACK_UID) {
+ throw new SecurityException("Invalid network stack UID: " + uid);
+ }
+
+ final int hasPermission =
+ pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
+ if (hasPermission != PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
+ if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+ Slog.wtf(TAG,
+ "Could not bind to network stack in-process, or in app with " + intent);
+ // TODO: crash/reboot system server if no network stack after a timeout ?
+ }
+ }
+
+ /**
+ * For non-system server clients, get the connector registered by the system server.
+ */
+ private INetworkStackConnector getRemoteConnector() {
+ // Block until the NetworkStack connector is registered in ServiceManager.
+ // <p>This is only useful for non-system processes that do not have a way to be notified of
+ // registration completion. Adding a callback system would be too heavy weight considering
+ // that the connector is registered on boot, so it is unlikely that a client would request
+ // it before it is registered.
+ // TODO: consider blocking boot on registration and simplify much of the logic in this class
+ IBinder connector;
+ try {
+ final long before = System.currentTimeMillis();
+ while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
+ Thread.sleep(20);
+ if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+ Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+ return null;
+ }
+ }
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+ return null;
+ }
+
+ return INetworkStackConnector.Stub.asInterface(connector);
+ }
+
+ private void requestConnector(@NonNull NetworkStackCallback request) {
+ // TODO: PID check.
+ final int caller = Binder.getCallingUid();
+ if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
+ // Don't even attempt to obtain the connector and give a nice error message
+ throw new SecurityException(
+ "Only the system server should try to bind to the network stack.");
+ }
+
+ if (!mNetworkStackStartRequested) {
+ // The network stack is not being started in this process, e.g. this process is not
+ // the system server. Get a remote connector registered by the system server.
+ final INetworkStackConnector connector = getRemoteConnector();
+ synchronized (mPendingNetStackRequests) {
+ mConnector = connector;
+ }
+ request.onNetworkStackConnected(connector);
+ return;
+ }
+
+ final INetworkStackConnector connector;
+ synchronized (mPendingNetStackRequests) {
+ connector = mConnector;
+ if (connector == null) {
+ mPendingNetStackRequests.add(request);
+ return;
+ }
+ }
+
+ request.onNetworkStackConnected(connector);
+ }
+}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 2a2a67a..bf917bf 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -23,8 +23,7 @@
import android.net.DhcpResultsParcelable;
import android.net.LinkProperties;
import android.net.LinkPropertiesParcelable;
-import android.net.NetworkStack;
-import android.net.ip.IIpClientCallbacks;
+import android.net.NetworkStackClient;
import android.os.ConditionVariable;
import java.io.FileDescriptor;
@@ -76,30 +75,17 @@
*
* <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
* {@link IIpClientCallbacks}.
- * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
+ * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
*/
public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
- context.getSystemService(NetworkStack.class)
- .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
- }
-
- /**
- * Create a new IpClient.
- *
- * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy}
- * instead of {@link IIpClientCallbacks}.
- * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
- */
- public static void makeIpClient(
- Context context, String ifName, IpClientCallbacksProxy callback) {
- context.getSystemService(NetworkStack.class)
- .makeIpClient(ifName, callback);
+ // TODO: migrate clients and remove context argument
+ NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback));
}
/**
* Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
*/
- public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
+ private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
protected final IpClientCallbacks mCb;
/**
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 7910c9a..34fc735 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -22,7 +22,6 @@
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
-import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
@@ -31,7 +30,7 @@
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.RouteInfo;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -132,10 +131,6 @@
}
public static class Dependencies {
- private final Context mContext;
- public Dependencies(Context context) {
- mContext = context;
- }
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
@@ -153,7 +148,7 @@
*/
public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb) {
- mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
+ NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index a3f36b7..3e5ce46 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -57,6 +57,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.utils.RandomAccessFileUtils;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -125,6 +127,7 @@
private File mTestDir;
private File mSuppressFile;
private File mActivatedFile;
+ private File mRememberActivatedFile;
@Before
public void setUp() throws Exception {
@@ -153,6 +156,8 @@
mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM);
TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile);
+ mRememberActivatedFile = new File(mTestDir, "rem-activate-" + NON_USER_SYSTEM);
+ TrampolineTestable.sRememberActivatedFiles.append(NON_USER_SYSTEM, mRememberActivatedFile);
mTrampoline = new TrampolineTestable(mContextMock);
}
@@ -411,6 +416,34 @@
}
@Test
+ public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
+ mTrampoline.initializeService();
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, false));
+ }
+
+ @Test
+ public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
+ mTrampoline.initializeService();
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+ assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true));
+ }
+
+ @Test
+ public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
+ mTrampoline.initializeService();
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+ assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true));
+ }
+
+ @Test
public void dataChanged_calledBeforeInitialize_ignored() throws Exception {
mTrampoline.dataChanged(PACKAGE_NAME);
verifyNoMoreInteractions(mBackupManagerServiceMock);
@@ -1291,6 +1324,7 @@
static BackupManagerService sBackupManagerServiceMock = null;
static File sSuppressFile = null;
static SparseArray<File> sActivatedFiles = new SparseArray<>();
+ static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
static UserManager sUserManagerMock = null;
private int mCreateServiceCallsCount = 0;
@@ -1314,6 +1348,11 @@
}
@Override
+ protected File getRememberActivatedFileForNonSystemUser(int userId) {
+ return sRememberActivatedFiles.get(userId);
+ }
+
+ @Override
protected File getActivatedFileForNonSystemUser(int userId) {
return sActivatedFiles.get(userId);
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
new file mode 100644
index 0000000..eaa9c45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.io.Files;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class FileUtilsTest {
+ private static File sTemporaryDir;
+ private File mTemporaryFile;
+
+ @BeforeClass
+ public static void setUpClass() {
+ sTemporaryDir = Files.createTempDir();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ if (sTemporaryDir != null) {
+ sTemporaryDir.delete();
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mTemporaryFile = new File(sTemporaryDir, "fileutilstest.txt");
+ }
+
+ /** Test that if file does not exist, {@link FileUtils#createNewFile()} creates the file. */
+ @Test
+ public void testEnsureFileExists_fileDoesNotAlreadyExist_getsCreated() {
+ assertThat(!mTemporaryFile.exists());
+
+ FileUtils.createNewFile(mTemporaryFile);
+
+ assertThat(mTemporaryFile.exists());
+ }
+
+ /** Test that if file does exist, {@link FileUtils#createNewFile()} does not error out. */
+ @Test
+ public void testEnsureFileExists_fileAlreadyExists_doesNotErrorOut() throws IOException {
+ mTemporaryFile.createNewFile();
+
+ FileUtils.createNewFile(mTemporaryFile);
+
+ assertThat(mTemporaryFile.exists());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
new file mode 100644
index 0000000..ca699bd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RandomAccessFileUtilsTest {
+ private File mTemporaryFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mTemporaryFile = File.createTempFile("fileutilstest", ".txt");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTemporaryFile != null) {
+ mTemporaryFile.delete();
+ }
+ }
+
+ /**
+ * Test that if we write true, we read back true.
+ */
+ @Test
+ public void testWriteTrue_readReturnsTrue() {
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+ assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+ }
+
+ /**
+ * Test that if we write false, we read back false.
+ */
+ @Test
+ public void testWriteFalse_readReturnsFalse() {
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+ assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+ }
+
+ /**
+ * Test that if we write true twice, we read back true.
+ */
+ @Test
+ public void testWriteTrueTwice_readReturnsTrue() {
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+ assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+ }
+
+ /**
+ * Test that if we write false twice, we read back false.
+ */
+ @Test
+ public void testWriteFalseTwice_readReturnsFalse() {
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+ assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+ }
+
+ /**
+ * Test that if we write true and then false, we read back false.
+ */
+ @Test
+ public void testWriteTrueFalse_readReturnsFalse() {
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+ assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+ }
+
+ /**
+ * Test that if we write false and then true, we read back true.
+ */
+ @Test
+ public void testWriteFalseTrue_readReturnsTrue() {
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+ RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+ assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index 9f1cbcd..6a937fa 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -98,6 +98,15 @@
}
@Test
+ public void testUpdateUserActivity_schedulesTheNextCheck() {
+ long now = SystemClock.uptimeMillis();
+ mNextDimming = now;
+ mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
+ long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L);
+ assertThat(nextTimeout).isEqualTo(mNextDimming + 5000L);
+ }
+
+ @Test
public void testOnUserActivity_ignoresAfterMaximumExtension() {
long now = SystemClock.uptimeMillis();
mAttentionDetector.onUserActivity(now - 15000L, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index a03d28b..763ea62 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -40,6 +40,7 @@
import android.view.IRecentsAnimationRunner;
import android.view.SurfaceControl;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
@@ -112,6 +113,7 @@
}
@Test
+ @FlakyTest(bugId = 117117823)
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 93f758c..b9440eb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1193,7 +1193,7 @@
}
@Override
- public void setTranscription(IVoiceInteractionService service, String transcription) {
+ public void setUiHints(IVoiceInteractionService service, Bundle hints) {
synchronized (this) {
enforceIsCurrentVoiceInteractionService(service);
@@ -1202,47 +1202,9 @@
final IVoiceInteractionSessionListener listener =
mVoiceInteractionSessionListeners.getBroadcastItem(i);
try {
- listener.onTranscriptionUpdate(transcription);
+ listener.onSetUiHints(hints);
} catch (RemoteException e) {
- Slog.e(TAG, "Error delivering voice transcription.", e);
- }
- }
- mVoiceInteractionSessionListeners.finishBroadcast();
- }
- }
-
- @Override
- public void clearTranscription(IVoiceInteractionService service, boolean immediate) {
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
-
- final int size = mVoiceInteractionSessionListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IVoiceInteractionSessionListener listener =
- mVoiceInteractionSessionListeners.getBroadcastItem(i);
- try {
- listener.onTranscriptionComplete(immediate);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering transcription complete event.", e);
- }
- }
- mVoiceInteractionSessionListeners.finishBroadcast();
- }
- }
-
- @Override
- public void setVoiceState(IVoiceInteractionService service, int state) {
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
-
- final int size = mVoiceInteractionSessionListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IVoiceInteractionSessionListener listener =
- mVoiceInteractionSessionListeners.getBroadcastItem(i);
- try {
- listener.onVoiceStateChange(state);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering voice state change.", e);
+ Slog.e(TAG, "Error delivering UI hints.", e);
}
}
mVoiceInteractionSessionListeners.finishBroadcast();
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 099015f..2aa4768 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -981,4 +981,13 @@
return false;
}
+
+ /**
+ * {@hide}
+ * Returns the recipient address(receiver) of this SMS message in String form or null if
+ * unavailable.
+ */
+ public String getRecipientAddress() {
+ return mWrappedSmsMessage.getRecipientAddress();
+ }
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 836a50b..94f26a8 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1236,7 +1236,7 @@
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
- return getActiveSubscriptionInfoList(false);
+ return getActiveSubscriptionInfoList(/* userVisibleonly */true);
}
/**
@@ -2858,15 +2858,24 @@
/**
* Whether system UI should hide a subscription. If it's a bundled opportunistic
* subscription, it shouldn't show up in anywhere in Settings app, dialer app,
- * or status bar.
+ * or status bar. Exception is if caller is carrier app, in which case they will
+ * want to see their own hidden subscriptions.
*
* @param info the subscriptionInfo to check against.
* @return true if this subscription should be hidden.
*
* @hide
*/
- public static boolean shouldHideSubscription(SubscriptionInfo info) {
- return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic());
+ private boolean shouldHideSubscription(SubscriptionInfo info) {
+ if (info == null) return false;
+
+ // If hasCarrierPrivileges or canManageSubscription returns true, it means caller
+ // has carrier privilege.
+ boolean hasCarrierPrivilegePermission = (info.isEmbedded() && canManageSubscription(info))
+ || TelephonyManager.from(mContext).hasCarrierPrivileges(info.getSubscriptionId());
+
+ return (!TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic()
+ && !hasCarrierPrivilegePermission);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 190eac4..ffdc4b6 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -41,6 +41,9 @@
@UnsupportedAppUsage
protected SmsAddress mOriginatingAddress;
+ /** {@hide} The address of the receiver */
+ protected SmsAddress mRecipientAddress;
+
/** {@hide} The message body as a string. May be null if the message isn't text */
@UnsupportedAppUsage
protected String mMessageBody;
@@ -457,4 +460,17 @@
return ted;
}
+
+ /**
+ * {@hide}
+ * Returns the receiver address of this SMS message in String
+ * form or null if unavailable
+ */
+ public String getRecipientAddress() {
+ if (mRecipientAddress == null) {
+ return null;
+ }
+
+ return mRecipientAddress.getAddressString();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 1da5eac..a31fa0b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -601,18 +601,24 @@
} else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
if (numberType == 2)
- Rlog.e(LOG_TAG, "TODO: Originating Addr is email id");
+ Rlog.e(LOG_TAG, "TODO: Addr is email id");
else
Rlog.e(LOG_TAG,
- "TODO: Originating Addr is data network address");
+ "TODO: Addr is data network address");
} else {
- Rlog.e(LOG_TAG, "Originating Addr is of incorrect type");
+ Rlog.e(LOG_TAG, "Addr is of incorrect type");
}
} else {
Rlog.e(LOG_TAG, "Incorrect Digit mode");
}
addr.origBytes = data;
- Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString());
+ Rlog.pii(LOG_TAG, "Addr=" + addr.toString());
+ mOriginatingAddress = addr;
+ if (parameterId == DESTINATION_ADDRESS) {
+ // Original address awlays indicates one sender's address for 3GPP2
+ // Here add recipient address support along with 3GPP
+ mRecipientAddress = addr;
+ }
break;
case ORIGINATING_SUB_ADDRESS:
case DESTINATION_SUB_ADDRESS:
@@ -667,7 +673,7 @@
}
/**
- * Parses a SMS message from its BearerData stream. (mobile-terminated only)
+ * Parses a SMS message from its BearerData stream.
*/
public void parseSms() {
// Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
@@ -697,16 +703,15 @@
}
if (mOriginatingAddress != null) {
- mOriginatingAddress.address = new String(mOriginatingAddress.origBytes);
- if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
- if (mOriginatingAddress.address.charAt(0) != '+') {
- mOriginatingAddress.address = "+" + mOriginatingAddress.address;
- }
- }
+ decodeSmsDisplayAddress(mOriginatingAddress);
if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
+ mOriginatingAddress.address);
}
+ if (mRecipientAddress != null) {
+ decodeSmsDisplayAddress(mRecipientAddress);
+ }
+
if (mBearerData.msgCenterTimeStamp != null) {
mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
}
@@ -731,7 +736,8 @@
status = mBearerData.errorClass << 8;
status |= mBearerData.messageStatus;
}
- } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) {
+ } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER
+ && mBearerData.messageType != BearerData.MESSAGE_TYPE_SUBMIT) {
throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
}
@@ -743,6 +749,16 @@
}
}
+ private void decodeSmsDisplayAddress(SmsAddress addr) {
+ addr.address = new String(addr.origBytes);
+ if (addr.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
+ if (addr.address.charAt(0) != '+') {
+ addr.address = "+" + addr.address;
+ }
+ }
+ Rlog.pii(LOG_TAG, " decodeSmsDisplayAddress = " + addr.address);
+ }
+
/**
* Parses a broadcast SMS, possibly containing a CMAS alert.
*
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 015efa6..19465a4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -71,9 +71,6 @@
// e.g. 23.040 9.2.2.1
private boolean mReplyPathPresent = false;
- /** The address of the receiver. */
- private GsmSmsAddress mRecipientAddress;
-
/**
* TP-Status - status of a previously submitted SMS.
* This field applies to SMS-STATUS-REPORT messages. 0 indicates success;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a4ec94..7b8c154 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1028,8 +1028,28 @@
</activity>
<activity
- android:name="PositionListenerActivity"
- android:label="RenderNode/PositionListener"
+ android:name="PositionListenerActivity"
+ android:label="RenderNode/PositionListener"
+ android:screenOrientation="fullSensor">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="CustomRenderer"
+ android:label="HardwareRenderer/HelloTakeSurface"
+ android:screenOrientation="fullSensor">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="MyLittleTextureView"
+ android:label="HardwareRenderer/MyLittleTextureView"
android:screenOrientation="fullSensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
new file mode 100644
index 0000000..60bd60f
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.HardwareRenderer;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+public class CustomRenderer extends Activity {
+ private RenderNode mContent = new RenderNode("CustomRenderer");
+ private HardwareRenderer mRenderer = new HardwareRenderer();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().takeSurface(mSurfaceCallbacks);
+ }
+
+ private SurfaceHolder.Callback2 mSurfaceCallbacks = new SurfaceHolder.Callback2() {
+
+ @Override
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ mContent.setLeftTopRightBottom(0, 0, width, height);
+ RecordingCanvas canvas = mContent.startRecording();
+ canvas.drawColor(Color.WHITE);
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setColor(Color.BLACK);
+ paint.setTextAlign(Paint.Align.CENTER);
+ paint.setTextSize(Math.min(width, height) * .05f);
+ canvas.drawText("Hello custom renderer!", width / 2, height / 2, paint);
+ mContent.endRecording();
+
+ mRenderer.setContentRoot(mContent);
+ mRenderer.setSurface(holder.getSurface());
+ mRenderer.createRenderRequest()
+ .setVsyncTime(System.nanoTime())
+ .setFrameCommitCallback(Runnable::run, () -> {
+ Log.d("CustomRenderer", "Frame committed!");
+ })
+ .syncAndDraw();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mRenderer.destroy();
+ }
+ };
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
new file mode 100644
index 0000000..8bd7d79
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.HardwareRenderer;
+import android.graphics.Outline;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
+import android.widget.ImageView;
+
+public class MyLittleTextureView extends Activity {
+ private RenderNode mContent = new RenderNode("CustomRenderer");
+ private HardwareRenderer mRenderer = new HardwareRenderer();
+ private ImageView mImageView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mImageView = new ImageView(this);
+ mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ setContentView(mImageView);
+
+ ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 3,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
+ mRenderer.setSurface(reader.getSurface());
+ mRenderer.setLightSourceAlpha(0.0f, 1.0f);
+ mRenderer.setLightSourceGeometry(100 / 2f, 0f, 800.0f, 20.0f);
+ mContent.setLeftTopRightBottom(0, 0, 100, 100);
+
+ Rect childRect = new Rect(25, 25, 65, 65);
+ RenderNode childNode = new RenderNode("shadowCaster");
+ childNode.setLeftTopRightBottom(childRect.left, childRect.top,
+ childRect.right, childRect.bottom);
+ Outline outline = new Outline();
+ outline.setRect(new Rect(0, 0, childRect.width(), childRect.height()));
+ outline.setAlpha(1f);
+ childNode.setOutline(outline);
+ {
+ Canvas canvas = childNode.startRecording();
+ canvas.drawColor(Color.BLUE);
+ }
+ childNode.endRecording();
+ childNode.setElevation(20f);
+
+ {
+ Canvas canvas = mContent.startRecording();
+ canvas.drawColor(Color.WHITE);
+ canvas.enableZ();
+ canvas.drawRenderNode(childNode);
+ canvas.disableZ();
+ }
+ mContent.endRecording();
+ mRenderer.setContentRoot(mContent);
+ mRenderer.createRenderRequest()
+ .setWaitForPresent(true)
+ .syncAndDraw();
+ Image image = reader.acquireNextImage();
+ Bitmap bitmap = Bitmap.wrapHardwareBuffer(image.getHardwareBuffer(),
+ ColorSpace.get(ColorSpace.Named.SRGB));
+ mImageView.setImageBitmap(bitmap);
+ image.close();
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e57d838..a10fb4e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,7 @@
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -123,7 +124,7 @@
import android.net.NetworkParcelable;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
@@ -245,7 +246,7 @@
@Mock INetworkStatsService mStatsService;
@Mock INetworkPolicyManager mNpm;
@Mock INetd mMockNetd;
- @Mock NetworkStack mNetworkStack;
+ @Mock NetworkStackClient mNetworkStack;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -901,11 +902,14 @@
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(uids);
- updateCapabilities();
+ updateCapabilities(null /* defaultNetwork */);
}
@Override
public int getNetId() {
+ if (mMockNetworkAgent == null) {
+ return NETID_UNSET;
+ }
return mMockNetworkAgent.getNetwork().netId;
}
@@ -927,12 +931,13 @@
}
@Override
- public void updateCapabilities() {
- if (!mConnected) return;
- super.updateCapabilities();
- // Because super.updateCapabilities will update the capabilities of the agent but not
- // the mock agent, the mock agent needs to know about them.
+ public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+ if (!mConnected) return null;
+ super.updateCapabilities(defaultNetwork);
+ // Because super.updateCapabilities will update the capabilities of the agent but
+ // not the mock agent, the mock agent needs to know about them.
copyCapabilitiesToNetworkAgent();
+ return new NetworkCapabilities(mNetworkCapabilities);
}
private void copyCapabilitiesToNetworkAgent() {
@@ -1077,6 +1082,11 @@
}
@Override
+ protected NetworkStackClient getNetworkStack() {
+ return mNetworkStack;
+ }
+
+ @Override
public WakeupMessage makeWakeupMessage(
Context context, Handler handler, String cmdName, int cmd, Object obj) {
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
@@ -4690,6 +4700,7 @@
vpnNetworkAgent.connect(false);
mMockVpn.connect();
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4722,6 +4733,7 @@
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
+ vpnNetworkAgent.setUids(ranges);
genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4755,12 +4767,11 @@
}
@Test
- public void testVpnWithAndWithoutInternet() {
+ public void testVpnWithoutInternet() {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
@@ -4782,11 +4793,30 @@
vpnNetworkAgent.disconnect();
defaultCallback.assertNoCallback();
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ mCm.unregisterNetworkCallback(defaultCallback);
+ }
+
+ @Test
+ public void testVpnWithInternet() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+
+ MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
mMockVpn.connect();
+
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -4794,14 +4824,6 @@
defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
- ranges.clear();
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
- mMockVpn.connect();
- defaultCallback.assertNoCallback();
-
mCm.unregisterNetworkCallback(defaultCallback);
}
@@ -4904,6 +4926,70 @@
}
@Test
+ public void testNullUnderlyingNetworks() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_VPN)
+ .build();
+ NetworkCapabilities nc;
+ mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+ vpnNetworkCallback.assertNoCallback();
+
+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.connect();
+ mMockVpn.setUids(ranges);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ // By default, VPN is set to track default network (i.e. its underlying networks is null).
+ // In case of no default network, VPN is considered metered.
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+ // Connect to Cell; Cell is the default network.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Connect to WiFi; WiFi is the new default.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+ && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Disconnect Cell. The default network did not change, so there shouldn't be any changes in
+ // the capabilities.
+ mCellNetworkAgent.disconnect();
+
+ // Disconnect wifi too. Now we have no default network.
+ mWiFiNetworkAgent.disconnect();
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ mMockVpn.disconnect();
+ }
+
+ @Test
public void testNetworkBlockedStatus() {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index e877a8f..5057443 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -38,7 +38,6 @@
import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
-import android.net.NetworkStack;
import android.os.INetworkManagementService;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -75,16 +74,12 @@
@Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
- @Mock NetworkStack mNetworkStack;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
- when(mCtx.getSystemServiceName(NetworkStack.class))
- .thenReturn(Context.NETWORK_STACK_SERVICE);
- when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a4a735d..533d7ad 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -195,10 +195,6 @@
}
public class MockIpServerDependencies extends IpServer.Dependencies {
- MockIpServerDependencies() {
- super(null);
- }
-
@Override
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
InterfaceParams ifParams) {
@@ -266,7 +262,7 @@
}
@Override
- public IpServer.Dependencies getIpServerDependencies(Context context) {
+ public IpServer.Dependencies getIpServerDependencies() {
return mIpServerDependencies;
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 46de3d0..f169d6b 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -566,7 +566,7 @@
final NetworkCapabilities caps = new NetworkCapabilities();
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -577,7 +577,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
new Network[] {mobile},
caps,
@@ -591,7 +591,7 @@
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -602,7 +602,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -613,7 +613,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
new Network[] {mobile, wifi},
caps,
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 72e57a1..8a1b21c 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -67,7 +67,7 @@
/** The network id in the wpa_supplicant */
private int mNetId;
- /** The frequency used by this group */
+ /** The frequency (in MHz) used by this group */
private int mFrequency;
/** P2P group started string pattern */
@@ -273,7 +273,7 @@
this.mNetId = netId;
}
- /** Get the operating frequency of the p2p group */
+ /** Get the operating frequency (in MHz) of the p2p group */
public int getFrequency() {
return mFrequency;
}