Merge "Removed data and voice RAT from service state"
diff --git a/Android.bp b/Android.bp
index b9b1bd8..5b8e6e1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -375,6 +375,7 @@
"core/java/android/view/IDisplayFoldListener.aidl",
"core/java/android/view/IGraphicsStats.aidl",
"core/java/android/view/IGraphicsStatsCallback.aidl",
+ "core/java/android/view/IInputMonitorHost.aidl",
"core/java/android/view/IInputFilter.aidl",
"core/java/android/view/IInputFilterHost.aidl",
"core/java/android/view/IOnKeyguardExitResult.aidl",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 2fdba0a..7e7b871 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -27,6 +27,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -130,6 +131,47 @@
}
}
+ /** Tests switching to an already-created, but no-longer-running, user. */
+ @Test
+ public void switchUser_stopped() throws Exception {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int startUser = mAm.getCurrentUser();
+ final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
+ final CountDownLatch latch = new CountDownLatch(1);
+ registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, testUser);
+ mRunner.resumeTiming();
+
+ mAm.switchUser(testUser);
+ boolean success = latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+
+ mRunner.pauseTiming();
+ attestTrue("Failed to achieve 2nd ACTION_USER_UNLOCKED for user " + testUser, success);
+ switchUser(startUser);
+ removeUser(testUser);
+ mRunner.resumeTiming();
+ }
+ }
+
+ /** Tests switching to an already-created already-running non-owner user. */
+ @Test
+ public void switchUser_running() throws Exception {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int startUser = mAm.getCurrentUser();
+ final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
+ mRunner.resumeTiming();
+
+ switchUser(testUser);
+
+ mRunner.pauseTiming();
+ attestTrue("Failed to switch to user " + testUser, mAm.isUserRunning(testUser));
+ switchUser(startUser);
+ removeUser(testUser);
+ mRunner.resumeTiming();
+ }
+ }
+
@Test
public void stopUser() throws Exception {
while (mRunner.keepRunning()) {
@@ -262,6 +304,35 @@
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
}
+ /**
+ * Creates a user and waits for its ACTION_USER_UNLOCKED.
+ * Then switches to back to the original user and waits for its switchUser() to finish.
+ *
+ * @param stopNewUser whether to stop the new user after switching to otherUser.
+ * @return userId of the newly created user.
+ */
+ private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws Exception {
+ final int origUser = mAm.getCurrentUser();
+ // First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED
+ final int testUser = mUm.createUser("TestUser", 0).id;
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, testUser);
+ mAm.switchUser(testUser);
+ attestTrue("Failed to achieve initial ACTION_USER_UNLOCKED for user " + testUser,
+ latch1.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS));
+
+ // Second, switch back to origUser, waiting merely for switchUser() to finish
+ switchUser(origUser);
+ attestTrue("Didn't switch back to user, " + origUser, origUser == mAm.getCurrentUser());
+
+ if (stopNewUser) {
+ stopUser(testUser, true);
+ attestFalse("Failed to stop user " + testUser, mAm.isUserRunning(testUser));
+ }
+
+ return testUser;
+ }
+
private void registerUserSwitchObserver(final CountDownLatch switchLatch,
final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
ActivityManager.getService().registerUserSwitchObserver(
@@ -313,4 +384,14 @@
mUsersToRemove.add(userId);
}
}
+
+ private void attestTrue(String message, boolean attestion) {
+ if (!attestion) {
+ Log.w(TAG, message);
+ }
+ }
+
+ private void attestFalse(String message, boolean attestion) {
+ attestTrue(message, !attestion);
+ }
}
diff --git a/api/current.txt b/api/current.txt
index 1394ba3..d4a1554 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34275,6 +34275,7 @@
ctor public Binder(@Nullable String);
method public void attachInterface(@Nullable android.os.IInterface, @Nullable String);
method public static final long clearCallingIdentity();
+ method public static final long clearCallingWorkSource();
method public void dump(@NonNull java.io.FileDescriptor, @Nullable String[]);
method protected void dump(@NonNull java.io.FileDescriptor, @NonNull java.io.PrintWriter, @Nullable String[]);
method public void dumpAsync(@NonNull java.io.FileDescriptor, @Nullable String[]);
@@ -34283,6 +34284,7 @@
method public static final int getCallingUid();
method public static final int getCallingUidOrThrow();
method @NonNull public static final android.os.UserHandle getCallingUserHandle();
+ method public static final int getCallingWorkSourceUid();
method @Nullable public String getInterfaceDescriptor();
method public boolean isBinderAlive();
method public static final void joinThreadPool();
@@ -34291,6 +34293,8 @@
method public boolean pingBinder();
method @Nullable public android.os.IInterface queryLocalInterface(@NonNull String);
method public static final void restoreCallingIdentity(long);
+ method public static final void restoreCallingWorkSource(long);
+ method public static final long setCallingWorkSourceUid(int);
method public final boolean transact(int, @NonNull android.os.Parcel, @Nullable android.os.Parcel, int) throws android.os.RemoteException;
method public boolean unlinkToDeath(@NonNull android.os.IBinder.DeathRecipient, int);
}
@@ -37069,12 +37073,6 @@
field public static final String CACHED_NUMBER_TYPE = "numbertype";
field public static final String CACHED_PHOTO_ID = "photo_id";
field public static final String CACHED_PHOTO_URI = "photo_uri";
- field public static final String CALL_ID_APP_NAME = "call_id_app_name";
- field public static final String CALL_ID_DESCRIPTION = "call_id_description";
- field public static final String CALL_ID_DETAILS = "call_id_details";
- field public static final String CALL_ID_NAME = "call_id_name";
- field public static final String CALL_ID_NUISANCE_CONFIDENCE = "call_id_nuisance_confidence";
- field public static final String CALL_ID_PACKAGE_NAME = "call_id_package_name";
field public static final String CALL_SCREENING_APP_NAME = "call_screening_app_name";
field public static final String CALL_SCREENING_COMPONENT_NAME = "call_screening_component_name";
field public static final android.net.Uri CONTENT_FILTER_URI;
@@ -43257,7 +43255,6 @@
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public int getCallCapabilities();
method public int getCallDirection();
- method @Nullable public android.telecom.CallIdentification getCallIdentification();
method public int getCallProperties();
method public String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
@@ -43339,34 +43336,6 @@
field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
- public final class CallIdentification implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public CharSequence getCallScreeningAppName();
- method @NonNull public String getCallScreeningPackageName();
- method @Nullable public CharSequence getDescription();
- method @Nullable public CharSequence getDetails();
- method @Nullable public CharSequence getName();
- method public int getNuisanceConfidence();
- method @Nullable public android.graphics.drawable.Icon getPhoto();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CONFIDENCE_LIKELY_NOT_NUISANCE = -1; // 0xffffffff
- field public static final int CONFIDENCE_LIKELY_NUISANCE = 1; // 0x1
- field public static final int CONFIDENCE_NOT_NUISANCE = -2; // 0xfffffffe
- field public static final int CONFIDENCE_NUISANCE = 2; // 0x2
- field public static final int CONFIDENCE_UNKNOWN = 0; // 0x0
- field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallIdentification> CREATOR;
- }
-
- public static final class CallIdentification.Builder {
- ctor public CallIdentification.Builder();
- method @NonNull public android.telecom.CallIdentification build();
- method @NonNull public android.telecom.CallIdentification.Builder setDescription(@Nullable CharSequence);
- method @NonNull public android.telecom.CallIdentification.Builder setDetails(@Nullable CharSequence);
- method @NonNull public android.telecom.CallIdentification.Builder setName(@Nullable CharSequence);
- method @NonNull public android.telecom.CallIdentification.Builder setNuisanceConfidence(int);
- method @NonNull public android.telecom.CallIdentification.Builder setPhoto(@Nullable android.graphics.drawable.Icon);
- }
-
public abstract class CallRedirectionService extends android.app.Service {
ctor public CallRedirectionService();
method public final void cancelCall();
@@ -43382,17 +43351,7 @@
ctor public CallScreeningService();
method public android.os.IBinder onBind(android.content.Intent);
method public abstract void onScreenCall(@NonNull android.telecom.Call.Details);
- method public final void provideCallIdentification(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallIdentification);
method public final void respondToCall(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallScreeningService.CallResponse);
- field public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED = "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
- field public static final int CALL_DURATION_LONG = 4; // 0x4
- field public static final int CALL_DURATION_MEDIUM = 3; // 0x3
- field public static final int CALL_DURATION_SHORT = 2; // 0x2
- field public static final int CALL_DURATION_VERY_SHORT = 1; // 0x1
- field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
- field public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
- field public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
- field public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
field public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
}
@@ -44001,7 +43960,6 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, String);
method @RequiresPermission(anyOf={android.Manifest.permission.CALL_PHONE, android.Manifest.permission.MANAGE_OWN_CALLS}) public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void reportNuisanceCallStatus(@NonNull android.net.Uri, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
diff --git a/api/system-current.txt b/api/system-current.txt
index b419851..d1018be 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1462,7 +1462,7 @@
method @Nullable public String getCategory();
method @NonNull public String getPackageName();
method @Nullable public String getTargetOverlayableName();
- method @Nullable public String getTargetPackageName();
+ method @NonNull public String getTargetPackageName();
method public int getUserId();
method public boolean isEnabled();
method public void writeToParcel(android.os.Parcel, int);
@@ -4232,13 +4232,19 @@
method @Nullable public java.net.InetAddress getGateway();
method @Nullable public android.net.LinkAddress getIpAddress();
method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
- method public void setDomains(@Nullable String);
- method public void setGateway(@Nullable java.net.InetAddress);
- method public void setIpAddress(@Nullable android.net.LinkAddress);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
}
+ public static final class StaticIpConfiguration.Builder {
+ ctor public StaticIpConfiguration.Builder();
+ method @NonNull public android.net.StaticIpConfiguration build();
+ method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
+ }
+
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
@@ -4288,37 +4294,6 @@
}
-package android.net.captiveportal {
-
- public final class CaptivePortalProbeResult {
- ctor public CaptivePortalProbeResult(int);
- ctor public CaptivePortalProbeResult(int, @Nullable String, @Nullable String);
- ctor public CaptivePortalProbeResult(int, @Nullable String, @Nullable String, @Nullable android.net.captiveportal.CaptivePortalProbeSpec);
- method public boolean isFailed();
- method public boolean isPartialConnectivity();
- method public boolean isPortal();
- method public boolean isSuccessful();
- field @NonNull public static final android.net.captiveportal.CaptivePortalProbeResult FAILED;
- field public static final int FAILED_CODE = 599; // 0x257
- field public static final android.net.captiveportal.CaptivePortalProbeResult PARTIAL;
- field public static final int PORTAL_CODE = 302; // 0x12e
- field @NonNull public static final android.net.captiveportal.CaptivePortalProbeResult SUCCESS;
- field public static final int SUCCESS_CODE = 204; // 0xcc
- field @Nullable public final String detectUrl;
- field @Nullable public final android.net.captiveportal.CaptivePortalProbeSpec probeSpec;
- field @Nullable public final String redirectUrl;
- }
-
- public abstract class CaptivePortalProbeSpec {
- method @NonNull public String getEncodedSpec();
- method @NonNull public abstract android.net.captiveportal.CaptivePortalProbeResult getResult(int, @Nullable String);
- method @NonNull public java.net.URL getUrl();
- method @NonNull public static java.util.Collection<android.net.captiveportal.CaptivePortalProbeSpec> parseCaptivePortalProbeSpecs(@NonNull String);
- method @Nullable public static android.net.captiveportal.CaptivePortalProbeSpec parseSpecOrNull(@Nullable String);
- }
-
-}
-
package android.net.metrics {
public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
@@ -5168,10 +5143,6 @@
}
public class Binder implements android.os.IBinder {
- method public static final long clearCallingWorkSource();
- method public static final int getCallingWorkSourceUid();
- method public static final void restoreCallingWorkSource(long);
- method public static final long setCallingWorkSourceUid(int);
method public static void setProxyTransactListener(@Nullable android.os.Binder.ProxyTransactListener);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index cb2dc07..67a26f3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -716,6 +716,11 @@
package android.content.res {
+ public final class AssetManager implements java.lang.AutoCloseable {
+ method @NonNull public String[] getApkPaths();
+ method @Nullable public java.util.Map<java.lang.String,java.lang.String> getOverlayableMap(String);
+ }
+
public final class Configuration implements java.lang.Comparable<android.content.res.Configuration> android.os.Parcelable {
field public int assetsSeq;
field public final android.app.WindowConfiguration windowConfiguration;
@@ -1320,13 +1325,19 @@
method @Nullable public java.net.InetAddress getGateway();
method @Nullable public android.net.LinkAddress getIpAddress();
method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
- method public void setDomains(@Nullable String);
- method public void setGateway(@Nullable java.net.InetAddress);
- method public void setIpAddress(@Nullable android.net.LinkAddress);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
}
+ public static final class StaticIpConfiguration.Builder {
+ ctor public StaticIpConfiguration.Builder();
+ method @NonNull public android.net.StaticIpConfiguration build();
+ method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
+ }
+
public final class TestNetworkInterface implements android.os.Parcelable {
ctor public TestNetworkInterface(android.os.ParcelFileDescriptor, String);
method public int describeContents();
@@ -1371,37 +1382,6 @@
}
-package android.net.captiveportal {
-
- public final class CaptivePortalProbeResult {
- ctor public CaptivePortalProbeResult(int);
- ctor public CaptivePortalProbeResult(int, @Nullable String, @Nullable String);
- ctor public CaptivePortalProbeResult(int, @Nullable String, @Nullable String, @Nullable android.net.captiveportal.CaptivePortalProbeSpec);
- method public boolean isFailed();
- method public boolean isPartialConnectivity();
- method public boolean isPortal();
- method public boolean isSuccessful();
- field @NonNull public static final android.net.captiveportal.CaptivePortalProbeResult FAILED;
- field public static final int FAILED_CODE = 599; // 0x257
- field public static final android.net.captiveportal.CaptivePortalProbeResult PARTIAL;
- field public static final int PORTAL_CODE = 302; // 0x12e
- field @NonNull public static final android.net.captiveportal.CaptivePortalProbeResult SUCCESS;
- field public static final int SUCCESS_CODE = 204; // 0xcc
- field @Nullable public final String detectUrl;
- field @Nullable public final android.net.captiveportal.CaptivePortalProbeSpec probeSpec;
- field @Nullable public final String redirectUrl;
- }
-
- public abstract class CaptivePortalProbeSpec {
- method @NonNull public String getEncodedSpec();
- method @NonNull public abstract android.net.captiveportal.CaptivePortalProbeResult getResult(int, @Nullable String);
- method @NonNull public java.net.URL getUrl();
- method @NonNull public static java.util.Collection<android.net.captiveportal.CaptivePortalProbeSpec> parseCaptivePortalProbeSpecs(@NonNull String);
- method @Nullable public static android.net.captiveportal.CaptivePortalProbeSpec parseSpecOrNull(@Nullable String);
- }
-
-}
-
package android.net.metrics {
public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 98ab666..680ccfc 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -112,6 +112,11 @@
return;
}
+ if ("activated".equals(op)) {
+ doActivated(userId);
+ return;
+ }
+
if (!isBackupActive(userId)) {
return;
}
@@ -200,6 +205,21 @@
return true;
}
+ private String activatedToString(boolean activated) {
+ return activated ? "activated" : "deactivated";
+ }
+
+ private void doActivated(@UserIdInt int userId) {
+ try {
+ System.out.println("Backup Manager currently "
+ + activatedToString(mBmgr.isBackupServiceActive(userId)));
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+
+ }
+
private String enableToString(boolean enabled) {
return enabled ? "enabled" : "disabled";
}
@@ -907,6 +927,7 @@
System.err.println(" bmgr cancel backups");
System.err.println(" bmgr init TRANSPORT...");
System.err.println(" bmgr activate BOOL");
+ System.err.println(" bmgr activated");
System.err.println("");
System.err.println("The '--user' option specifies the user on which the operation is run.");
System.err.println("It must be the first argument before the operation.");
@@ -978,6 +999,9 @@
System.err.println("If the argument is 'true' it will be activated, otherwise it will be");
System.err.println("deactivated. When deactivated, the service will not be running and no");
System.err.println("operations can be performed until activation.");
+ System.err.println("");
+ System.err.println("The 'activated' command reports the current activated/deactivated");
+ System.err.println("state of the backup mechanism.");
}
private static class BackupMonitor extends IBackupManagerMonitor.Stub {
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 6d5fe7b..49470b4 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -269,12 +269,10 @@
std::string ConcatPolicies(const std::vector<std::string>& policies) {
std::string message;
for (const std::string& policy : policies) {
- if (message.empty()) {
- message.append(policy);
- } else {
- message.append(policy);
+ if (!message.empty()) {
message.append("|");
}
+ message.append(policy);
}
return message;
diff --git a/cmds/incidentd/src/Broadcaster.cpp b/cmds/incidentd/src/Broadcaster.cpp
index 39e5393..63464f2 100644
--- a/cmds/incidentd/src/Broadcaster.cpp
+++ b/cmds/incidentd/src/Broadcaster.cpp
@@ -22,6 +22,7 @@
#include <android/os/DropBoxManager.h>
#include <binder/IServiceManager.h>
+#include <thread>
namespace android {
namespace os {
@@ -391,13 +392,20 @@
return NO_ERROR;
}
- // Start a thread to write the data to dropbox.
- int readFd = -1;
- err = file->startFilteringData(&readFd, args);
- if (err != NO_ERROR) {
- return err;
+ int fds[2];
+ if (pipe(fds) != 0) {
+ ALOGW("Error opening pipe to filter incident report: %s", file->getDataFileName().c_str());
+ return NO_ERROR;
}
+ int readFd = fds[0];
+ int writeFd = fds[1];
+
+ // spawn a thread to write the data. Release the writeFd ownership to the thread.
+ thread th([file, writeFd, args]() { file->startFilteringData(writeFd, args); });
+
+ th.detach();
+
// Takes ownership of readFd.
Status status = dropbox->addFile(String16("incident"), readFd, 0);
if (!status.isOk()) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 4ba31b4..b4021d1 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -32,6 +32,7 @@
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
+#include <thread>
#include <unistd.h>
@@ -117,7 +118,8 @@
}
static string build_uri(const string& pkg, const string& cls, const string& id) {
- return "build_uri not implemented " + pkg + "/" + cls + "/" + id;
+ return "content://android.os.IncidentManager/pending?pkg="
+ + pkg + "&receiver=" + cls + "&r=" + id;
}
// ================================================================================
@@ -358,17 +360,21 @@
IncidentReportArgs args;
sp<ReportFile> file = mWorkDirectory->getReport(pkg, cls, id, &args);
if (file != nullptr) {
- int fd;
- err = file->startFilteringData(&fd, args);
- if (err != 0) {
- ALOGW("Error reading data file that we think should exist: %s",
- file->getDataFileName().c_str());
+ // Create pipe
+ int fds[2];
+ if (pipe(fds) != 0) {
+ ALOGW("Error opening pipe to filter incident report: %s",
+ file->getDataFileName().c_str());
return Status::ok();
}
-
result->setTimestampNs(file->getTimestampNs());
result->setPrivacyPolicy(file->getEnvelope().privacy_policy());
- result->takeFileDescriptor(fd);
+ result->takeFileDescriptor(fds[0]);
+ int writeFd = fds[1];
+ // spawn a thread to write the data. Release the writeFd ownership to the thread.
+ thread th([file, writeFd, args]() { file->startFilteringData(writeFd, args); });
+
+ th.detach();
}
return Status::ok();
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index e773e74..218c1b2 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -551,7 +551,7 @@
buf++) {
// If there was an error now, there will be an error later and we will remove
// it from the list then.
- write_header_section(request->getFd(), *buf);
+ write_header_section(request->getFd(), buf->data(), buf->size());
}
});
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index aa376dd..ae640c6 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -18,6 +18,7 @@
#include "WorkDirectory.h"
+#include "proto_util.h"
#include "PrivacyFilter.h"
#include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -64,6 +65,9 @@
*/
const ComponentName DROPBOX_SENTINEL("android", "DROPBOX");
+/** metadata field id in IncidentProto */
+const int FIELD_ID_INCIDENT_METADATA = 2;
+
/**
* Read a protobuf from disk into the message.
*/
@@ -386,65 +390,53 @@
}
}
-status_t ReportFile::startFilteringData(int* fd, const IncidentReportArgs& args) {
+status_t ReportFile::startFilteringData(int writeFd, const IncidentReportArgs& args) {
// Open data file.
int dataFd = open(mDataFileName.c_str(), O_RDONLY | O_CLOEXEC);
if (dataFd < 0) {
+ ALOGW("Error opening incident report '%s' %s", getDataFileName().c_str(), strerror(-errno));
+ close(writeFd);
return -errno;
}
// Check that the size on disk is what we thought we wrote.
struct stat st;
if (fstat(dataFd, &st) != 0) {
+ ALOGW("Error running fstat incident report '%s' %s", getDataFileName().c_str(),
+ strerror(-errno));
+ close(writeFd);
return -errno;
}
if (st.st_size != mEnvelope.data_file_size()) {
ALOGW("File size mismatch. Envelope says %" PRIi64 " bytes but data file is %" PRIi64
- " bytes: %s", (int64_t)mEnvelope.data_file_size(), st.st_size,
- mDataFileName.c_str());
+ " bytes: %s",
+ (int64_t)mEnvelope.data_file_size(), st.st_size, mDataFileName.c_str());
ALOGW("Removing incident report");
mWorkDirectory->remove(this);
+ close(writeFd);
return BAD_VALUE;
}
- // Create pipe
- int fds[2];
- if (pipe(fds) != 0) {
- ALOGW("Error opening pipe to filter incident report: %s", getDataFileName().c_str());
- return -errno;
+ status_t err;
+
+ for (const auto& report : mEnvelope.report()) {
+ for (const auto& header : report.header()) {
+ write_header_section(writeFd,
+ reinterpret_cast<const uint8_t*>(header.c_str()), header.size());
+ }
}
- *fd = fds[0];
- int writeFd = fds[1];
+ if (mEnvelope.has_metadata()) {
+ write_section(writeFd, FIELD_ID_INCIDENT_METADATA, mEnvelope.metadata());
+ }
- // Spawn off a thread to do the filtering and writing
- thread th([this, dataFd, writeFd, args]() {
- ALOGD("worker thread started dataFd=%d writeFd=%d", dataFd, writeFd);
- status_t err;
+ err = filter_and_write_report(writeFd, dataFd, mEnvelope.privacy_policy(), args);
+ if (err != NO_ERROR) {
+ ALOGW("Error writing incident report '%s' to dropbox: %s", getDataFileName().c_str(),
+ strerror(-err));
+ }
- err = filter_and_write_report(writeFd, dataFd, mEnvelope.privacy_policy(), args);
- close(writeFd);
-
- if (err != NO_ERROR) {
- ALOGW("Error writing incident report '%s' to dropbox: %s", getDataFileName().c_str(),
- strerror(-err));
- // If there's an error here, there will also be an error returned from
- // addFile, so we'll use that error to reschedule the send_to_dropbox.
- // If the file is corrupted, we will put some logs in logcat, but won't
- // actually return an error.
- return;
- }
- });
-
- // Better would be to join this thread after write is back, but there is no
- // timeout parameter for that, which means we can't clean up if system server
- // is stuck. Better is to leak the thread, which will eventually clean itself
- // up after system server eventually dies, which it probably will.
- th.detach();
-
- // If the thread fails to start, we should return an error, but the thread
- // class doesn't give us a good way to determine that. Just pretend everything
- // is ok.
+ close(writeFd);
return NO_ERROR;
}
@@ -501,7 +493,7 @@
// ================================================================================
-//
+//
WorkDirectory::WorkDirectory()
:mDirectory("/data/misc/incidents"),
diff --git a/cmds/incidentd/src/WorkDirectory.h b/cmds/incidentd/src/WorkDirectory.h
index e344371..3c6a2f2 100644
--- a/cmds/incidentd/src/WorkDirectory.h
+++ b/cmds/incidentd/src/WorkDirectory.h
@@ -135,14 +135,14 @@
void closeDataFile();
/**
- * Spawn a thread to start writing and filtering data to a pipe, the read end of which
- * will be returned in fd. This thread will be detached and run until somebody finishes
- * reading from the fd or closes it. If there is an error, returns it and you will not
- * get an fd.
+ * Use the privacy and section configuration from the args parameter to filter data, write
+ * to [writeFd] and take the ownership of [writeFd].
*
- * Use the privacy and section configuraiton from the args parameter.
+ * Note: this call is blocking. When the writeFd is a pipe fd for IPC, caller should make sure
+ * it's called on a separate thread so that reader can start to read without waiting for writer
+ * to finish writing (which may not happen due to pipe buffer overflow).
*/
- status_t startFilteringData(int* fd, const IncidentReportArgs& args);
+ status_t startFilteringData(int writeFd, const IncidentReportArgs& args);
/**
* Get the name of the data file on disk.
diff --git a/cmds/incidentd/src/proto_util.cpp b/cmds/incidentd/src/proto_util.cpp
index be2f24f..4e8ff71 100644
--- a/cmds/incidentd/src/proto_util.cpp
+++ b/cmds/incidentd/src/proto_util.cpp
@@ -33,11 +33,10 @@
// special section ids
const int FIELD_ID_INCIDENT_HEADER = 1;
-status_t write_header_section(int fd, const vector<uint8_t>& buf) {
+status_t write_header_section(int fd, const uint8_t* buf, size_t bufSize) {
status_t err;
- const size_t bufSize = buf.size();
- if (buf.empty()) {
+ if (bufSize == 0) {
return NO_ERROR;
}
@@ -46,7 +45,7 @@
return err;
}
- err = WriteFully(fd, (uint8_t const*)buf.data(), bufSize);
+ err = WriteFully(fd, buf, bufSize);
if (err != NO_ERROR) {
return err;
}
diff --git a/cmds/incidentd/src/proto_util.h b/cmds/incidentd/src/proto_util.h
index b9df6cb..2c0ab48 100644
--- a/cmds/incidentd/src/proto_util.h
+++ b/cmds/incidentd/src/proto_util.h
@@ -32,7 +32,7 @@
/**
* Write the IncidentHeaderProto section
*/
-status_t write_header_section(int fd, const vector<uint8_t>& buf);
+status_t write_header_section(int fd, const uint8_t* buf, size_t bufSize);
/**
* Write the prologue for a section in the incident report
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 8de1881..305a4ce 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -66,7 +66,7 @@
void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
const bool include_current_partial_bucket, const bool erase_data,
- const DumpReportReason dumpReportReason,
+ const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency,
vector<uint8_t>* outData);
void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
@@ -260,6 +260,9 @@
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
};
} // namespace statsd
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cadc3a0..dc4413f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -259,6 +259,7 @@
BiometricEnrolled biometric_enrolled = 184;
SystemServerWatchdogOccurred system_server_watchdog_occurred = 185;
TombStoneOccurred tomb_stone_occurred = 186;
+ BluetoothClassOfDeviceReported bluetooth_class_of_device_reported = 187;
}
// Pulled events will start at field 10000.
@@ -2142,6 +2143,29 @@
}
/**
+ * Logs when Class of Device (CoD) value is learnt for a device during pairing or connection
+ *
+ * Logged from:
+ * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java
+ * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java
+ *
+ */
+message BluetoothClassOfDeviceReported {
+ // An identifier that can be used to match events for this device.
+ // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+ // Salt: Randomly generated 256 bit value
+ // Hash algorithm: HMAC-SHA256
+ // Size: 32 byte
+ // Default: null or empty if this is a server listener socket
+ optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Class of Device (CoD) value including both Major, Minor device class and service class
+ // Defined in: https://www.bluetooth.com/specifications/assigned-numbers/baseband
+ // Also defined in: https://developer.android.com/reference/android/bluetooth/BluetoothClass
+ // Default: 0
+ optional int32 class_of_device = 2;
+}
+
+/**
* Logs when something is plugged into or removed from the USB-C connector.
*
* Logged from:
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 4cf5333..5ed95ed 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -70,11 +70,11 @@
bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
bool isActive = mEventActivationMap.empty();
for (auto& it : mEventActivationMap) {
- if (it.second.state == ActivationState::kActive &&
- elapsedTimestampNs > it.second.ttl_ns + it.second.activation_ns) {
- it.second.state = ActivationState::kNotActive;
+ if (it.second->state == ActivationState::kActive &&
+ elapsedTimestampNs > it.second->ttl_ns + it.second->activation_ns) {
+ it.second->state = ActivationState::kNotActive;
}
- if (it.second.state == ActivationState::kActive) {
+ if (it.second->state == ActivationState::kActive) {
isActive = true;
}
}
@@ -92,7 +92,8 @@
}
}
-void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) {
+void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds,
+ int deactivationTrackerIndex) {
std::lock_guard<std::mutex> lock(mMutex);
// When a metric producer does not depend on any activation, its mIsActive is true.
// Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
@@ -100,7 +101,12 @@
if (mEventActivationMap.empty()) {
mIsActive = false;
}
- mEventActivationMap[activationTrackerIndex].ttl_ns = ttl_seconds * NS_PER_SEC;
+ std::shared_ptr<Activation> activation = std::make_shared<Activation>();
+ activation->ttl_ns = ttl_seconds * NS_PER_SEC;
+ mEventActivationMap.emplace(activationTrackerIndex, activation);
+ if (-1 != deactivationTrackerIndex) {
+ mEventDeactivationMap.emplace(deactivationTrackerIndex, activation);
+ }
}
void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
@@ -109,27 +115,35 @@
return;
}
if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT &&
- it->second.state == ActivationState::kNotActive) {
- it->second.state = ActivationState::kActiveOnBoot;
+ it->second->state == ActivationState::kNotActive) {
+ it->second->state = ActivationState::kActiveOnBoot;
return;
}
- it->second.activation_ns = elapsedTimestampNs;
- it->second.state = ActivationState::kActive;
+ it->second->activation_ns = elapsedTimestampNs;
+ it->second->state = ActivationState::kActive;
mIsActive = true;
}
+void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
+ auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
+ if (it == mEventDeactivationMap.end()) {
+ return;
+ }
+ it->second->state = ActivationState::kNotActive;
+}
+
void MetricProducer::setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs) {
if (mEventActivationMap.size() == 0) {
return;
}
for (auto& pair : mEventActivationMap) {
auto& activation = pair.second;
- if (activation.ttl_ns >= remainingTtlNs) {
- activation.activation_ns = currentTimeNs + remainingTtlNs - activation.ttl_ns;
- activation.state = kActive;
+ if (activation->ttl_ns >= remainingTtlNs) {
+ activation->activation_ns = currentTimeNs + remainingTtlNs - activation->ttl_ns;
+ activation->state = kActive;
mIsActive = true;
- VLOG("setting new activation time to %lld, %lld, %lld",
- (long long)activation.activation_ns, (long long)currentTimeNs,
+ VLOG("setting new activation->time to %lld, %lld, %lld",
+ (long long)activation->activation_ns, (long long)currentTimeNs,
(long long)remainingTtlNs);
return;
}
@@ -140,8 +154,8 @@
int64_t MetricProducer::getRemainingTtlNsLocked(int64_t currentTimeNs) const {
int64_t maxTtl = 0;
for (const auto& activation : mEventActivationMap) {
- if (activation.second.state == kActive) {
- maxTtl = std::max(maxTtl, activation.second.ttl_ns + activation.second.activation_ns -
+ if (activation.second->state == kActive) {
+ maxTtl = std::max(maxTtl, activation.second->ttl_ns + activation.second->activation_ns -
currentTimeNs);
}
}
@@ -153,9 +167,9 @@
return;
}
for (auto& activation : mEventActivationMap) {
- if (activation.second.state == kActiveOnBoot) {
- activation.second.state = kActive;
- activation.second.activation_ns = currentTimeNs;
+ if (activation.second->state == kActiveOnBoot) {
+ activation.second->state = kActive;
+ activation.second->activation_ns = currentTimeNs;
mIsActive = true;
}
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 046f996..70fbd47 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -228,6 +228,11 @@
activateLocked(activationTrackerIndex, elapsedTimestampNs);
}
+ void cancelEventActivation(int deactivationTrackerIndex) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ cancelEventActivationLocked(deactivationTrackerIndex);
+ }
+
bool isActive() const {
std::lock_guard<std::mutex> lock(mMutex);
return isActiveLocked();
@@ -238,7 +243,8 @@
prepActiveForBootIfNecessaryLocked(currentTimeNs);
}
- void addActivation(int activationTrackerIndex, int64_t ttl_seconds);
+ void addActivation(int activationTrackerIndex, int64_t ttl_seconds,
+ int deactivationTrackerIndex = -1);
inline void setActivationType(const MetricActivation::ActivationType& activationType) {
mActivationType = activationType;
@@ -263,6 +269,7 @@
bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
+ void cancelEventActivationLocked(int deactivationTrackerIndex);
inline bool isActiveLocked() const {
return mIsActive;
@@ -391,13 +398,19 @@
};
// When the metric producer has multiple activations, these activations are ORed to determine
// whether the metric producer is ready to generate metrics.
- std::unordered_map<int, Activation> mEventActivationMap;
+ std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
+
+ // Maps index of atom matcher for deactivation to Activation struct.
+ std::unordered_map<int, std::shared_ptr<Activation>> mEventDeactivationMap;
bool mIsActive;
MetricActivation::ActivationType mActivationType;
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 4b3bfd3..095f9dd 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -74,7 +74,8 @@
timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
- mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds);
+ mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
+ mMetricIndexesWithActivation, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
mVersionStringsInReport = config.version_strings_in_metric_report();
@@ -255,7 +256,7 @@
bool MetricsManager::checkLogCredentials(const LogEvent& event) {
if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
- android::util::AtomsInfo::kWhitelistedAtoms.end())
+ android::util::AtomsInfo::kWhitelistedAtoms.end())
{
return true;
}
@@ -344,24 +345,61 @@
int64_t eventTimeNs = event.GetElapsedTimestampNs();
bool isActive = mIsAlwaysActive;
- for (int metric : mMetricIndexesWithActivation) {
- mAllMetricProducers[metric]->flushIfExpire(eventTimeNs);
- isActive |= mAllMetricProducers[metric]->isActive();
+
+ // Set of metrics that are still active after flushing.
+ unordered_set<int> activeMetricsIndices;
+
+ // Update state of all metrics w/ activation conditions as of eventTimeNs.
+ for (int metricIndex : mMetricIndexesWithActivation) {
+ const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
+ metric->flushIfExpire(eventTimeNs);
+ if (metric->isActive()) {
+ // If this metric w/ activation condition is still active after
+ // flushing, remember it.
+ activeMetricsIndices.insert(metricIndex);
+ }
}
- mIsActive = isActive;
+ mIsActive = isActive || !activeMetricsIndices.empty();
if (mTagIds.find(tagId) == mTagIds.end()) {
- // not interesting...
+ // Not interesting...
return;
}
vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed);
+ // Evaluate all atom matchers.
for (auto& matcher : mAllAtomMatchers) {
matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
}
+ // Set of metrics that received an activation cancellation.
+ unordered_set<int> metricIndicesWithCanceledActivations;
+
+ // Determine which metric activations received a cancellation and cancel them.
+ for (const auto& it : mDeactivationAtomTrackerToMetricMap) {
+ if (matcherCache[it.first] == MatchingState::kMatched) {
+ for (int metricIndex : it.second) {
+ mAllMetricProducers[metricIndex]->cancelEventActivation(it.first);
+ metricIndicesWithCanceledActivations.insert(metricIndex);
+ }
+ }
+ }
+
+ // Determine whether any metrics are no longer active after cancelling metric activations.
+ for (const int metricIndex : metricIndicesWithCanceledActivations) {
+ const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
+ metric->flushIfExpire(eventTimeNs);
+ if (!metric->isActive()) {
+ activeMetricsIndices.erase(metricIndex);
+ }
+ }
+
+ isActive |= !activeMetricsIndices.empty();
+
+
+ // Determine which metric activations should be turned on and turn them on
for (const auto& it : mActivationAtomTrackerToMetricMap) {
if (matcherCache[it.first] == MatchingState::kMatched) {
for (int metricIndex : it.second) {
@@ -406,12 +444,12 @@
if (pair != mConditionToMetricMap.end()) {
auto& metricList = pair->second;
for (auto metricIndex : metricList) {
- // metric cares about non sliced condition, and it's changed.
+ // Metric cares about non sliced condition, and it's changed.
// Push the new condition to it directly.
if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
eventTimeNs);
- // metric cares about sliced conditions, and it may have changed. Send
+ // Metric cares about sliced conditions, and it may have changed. Send
// notification, and the metric can query the sliced conditions that are
// interesting to it.
} else {
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 3904460..d317f8e 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -225,18 +225,21 @@
// The following map is initialized from the statsd_config.
- // maps from the index of the LogMatchingTracker to index of MetricProducer.
+ // Maps from the index of the LogMatchingTracker to index of MetricProducer.
std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
- // maps from LogMatchingTracker to ConditionTracker
+ // Maps from LogMatchingTracker to ConditionTracker
std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
- // maps from ConditionTracker to MetricProducer
+ // Maps from ConditionTracker to MetricProducer
std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
- // maps from life span triggering event to MetricProducers.
+ // Maps from life span triggering event to MetricProducers.
std::unordered_map<int, std::vector<int>> mActivationAtomTrackerToMetricMap;
+ // Maps deactivation triggering event to MetricProducers.
+ std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap;
+
std::vector<int> mMetricIndexesWithActivation;
void initLogSourceWhiteList();
@@ -281,6 +284,9 @@
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 463b5a0..082382c 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -711,6 +711,7 @@
const unordered_map<int64_t, int> &metricProducerMap,
vector<sp<MetricProducer>>& allMetricProducers,
unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
vector<int>& metricsWithActivation) {
for (int i = 0; i < config.metric_activation_size(); ++i) {
const MetricActivation& metric_activation = config.metric_activation(i);
@@ -725,8 +726,8 @@
ALOGE("Invalid metric tracker index.");
return false;
}
- allMetricProducers[metricTrackerIndex]->setActivationType(
- metric_activation.activation_type());
+ const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex];
+ metric->setActivationType(metric_activation.activation_type());
metricsWithActivation.push_back(metricTrackerIndex);
for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
const EventActivation& activation = metric_activation.event_activation(j);
@@ -738,8 +739,22 @@
const int atomMatcherIndex = logTrackerIt->second;
activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
metricTrackerIndex);
- allMetricProducers[metricTrackerIndex]->addActivation(
- atomMatcherIndex, activation.ttl_seconds());
+
+ if (activation.has_deactivation_atom_matcher_id()) {
+ auto deactivationAtomMatcherIt =
+ logEventTrackerMap.find(activation.deactivation_atom_matcher_id());
+ if (deactivationAtomMatcherIt == logEventTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event deactivation.");
+ return false;
+ }
+ const int deactivationMatcherIndex = deactivationAtomMatcherIt->second;
+ deactivationAtomTrackerToMetricMap[deactivationMatcherIndex]
+ .push_back(metricTrackerIndex);
+ metric->addActivation(atomMatcherIndex, activation.ttl_seconds(),
+ deactivationMatcherIndex);
+ } else {
+ metric->addActivation(atomMatcherIndex, activation.ttl_seconds());
+ }
}
}
return true;
@@ -759,6 +774,7 @@
unordered_map<int, std::vector<int>>& trackerToMetricMap,
unordered_map<int, std::vector<int>>& trackerToConditionMap,
unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
vector<int>& metricsWithActivation,
std::set<int64_t>& noReportMetricIds) {
unordered_map<int64_t, int> logTrackerMap;
@@ -795,7 +811,8 @@
return false;
}
if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap,
- allMetricProducers, activationAtomTrackerToMetricMap, metricsWithActivation)) {
+ allMetricProducers, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
ALOGE("initMetricActivations failed");
return false;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 9ffceda..028231f 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -108,8 +108,9 @@
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- unordered_map<int, std::vector<int>>& lifeSpanEventTrackerToMetricMap,
- vector<int>& metricsWithLifeSpan,
+ unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
std::set<int64_t>& noReportMetricIds);
bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 0e91f52..257e65e 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -380,6 +380,7 @@
message EventActivation {
optional int64 atom_matcher_id = 1;
optional int64 ttl_seconds = 2;
+ optional int64 deactivation_atom_matcher_id = 3;
}
message MetricActivation {
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index f8184d8..71adc57 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -282,8 +282,9 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
- vector<int> metricsWithLifeSpan;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
std::set<int64_t> noReportMetricIds;
EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -291,8 +292,8 @@
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
- lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
- noReportMetricIds));
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, noReportMetricIds));
EXPECT_EQ(1u, allMetricProducers.size());
EXPECT_EQ(1u, allAnomalyTrackers.size());
EXPECT_EQ(1u, noReportMetricIds.size());
@@ -313,8 +314,9 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
- vector<int> metricsWithLifeSpan;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -322,8 +324,8 @@
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
- lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
- noReportMetricIds));
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, noReportMetricIds));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -341,8 +343,9 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
- vector<int> metricsWithLifeSpan;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -350,8 +353,8 @@
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
- lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
- noReportMetricIds));
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -369,16 +372,17 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
- vector<int> metricsWithLifeSpan;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
- lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
- noReportMetricIds));
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -396,16 +400,17 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
- vector<int> metricsWithLifeSpan;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
- lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
- noReportMetricIds));
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, noReportMetricIds));
}
TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -423,8 +428,9 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
- vector<int> metricsWithLifeSpan;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -432,8 +438,8 @@
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
- lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
- noReportMetricIds));
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, noReportMetricIds));
}
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -451,8 +457,9 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
- vector<int> metricsWithLifeSpan;
+ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -460,8 +467,8 @@
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
- lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
- noReportMetricIds));
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, noReportMetricIds));
}
#else
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 88aa180..91e282a 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -610,26 +610,26 @@
// 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);
- EXPECT_EQ(0, activation1003.activation_ns);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
+ EXPECT_EQ(0, activation1003->activation_ns);
EXPECT_FALSE(metricProducer1005->isActive());
const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
- EXPECT_EQ(100 * NS_PER_SEC, activation1005.ttl_ns);
- EXPECT_EQ(0, activation1005.activation_ns);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns);
+ EXPECT_EQ(0, activation1005->activation_ns);
EXPECT_FALSE(metricProducer1006->isActive());
const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
- EXPECT_EQ(200 * NS_PER_SEC, activation1006.ttl_ns);
- EXPECT_EQ(0, activation1006.activation_ns);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns);
+ EXPECT_EQ(0, activation1006->activation_ns);
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_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->activation_ns);
EXPECT_TRUE(metricProducer1005->isActive());
- EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns);
+ EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->activation_ns);
EXPECT_TRUE(metricProducer1006->isActive());
- EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns);
+ EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->activation_ns);
// Make sure no more broadcasts have happened.
EXPECT_EQ(broadcastCount, 1);
@@ -696,17 +696,17 @@
EXPECT_TRUE(metricProducer2->isActive());
const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second;
- EXPECT_EQ(100 * NS_PER_SEC, activation1.ttl_ns);
- EXPECT_EQ(0, activation1.activation_ns);
- EXPECT_EQ(kNotActive, activation1.state);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+ EXPECT_EQ(0, activation1->activation_ns);
+ EXPECT_EQ(kNotActive, activation1->state);
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
processor->OnLogEvent(event.get());
EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1.activation_ns);
- EXPECT_EQ(kActiveOnBoot, activation1.state);
+ EXPECT_EQ(0, activation1->activation_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1->state);
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
@@ -746,14 +746,14 @@
EXPECT_TRUE(metricProducer1002->isActive());
const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second;
- EXPECT_EQ(100 * NS_PER_SEC, activation1001.ttl_ns);
- EXPECT_EQ(0, activation1001.activation_ns);
- EXPECT_EQ(kNotActive, activation1001.state);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
+ EXPECT_EQ(0, activation1001->activation_ns);
+ EXPECT_EQ(kNotActive, activation1001->state);
processor2->LoadMetricsActivationFromDisk();
EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001.ttl_ns, activation1001.activation_ns);
+ EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->activation_ns);
}
#else
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index 7fb43f8a..bf52bb0 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -31,9 +31,9 @@
StatsdConfig CreateStatsdConfig() {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
auto crashMatcher = CreateProcessCrashAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
*config.add_atom_matcher() = saverModeMatcher;
*config.add_atom_matcher() = crashMatcher;
@@ -60,13 +60,149 @@
return config;
}
+StatsdConfig CreateStatsdConfigWithOneDeactivation() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
+ auto crashMatcher = CreateProcessCrashAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
+
+ *config.add_atom_matcher() = saverModeMatcher;
+ *config.add_atom_matcher() = crashMatcher;
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = brightnessChangedMatcher;
+
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(crashMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+ countMetric->mutable_dimensions_in_what()->set_field(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
+
+ auto metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(metricId);
+ auto event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(saverModeMatcher.id());
+ event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
+ event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
+ auto event_activation2 = metric_activation1->add_event_activation();
+ event_activation2->set_atom_matcher_id(screenOnMatcher.id());
+ event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
+
+ return config;
+}
+
+StatsdConfig CreateStatsdConfigWithTwoDeactivations() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
+ auto crashMatcher = CreateProcessCrashAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher();
+ brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2"));
+
+ *config.add_atom_matcher() = saverModeMatcher;
+ *config.add_atom_matcher() = crashMatcher;
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = brightnessChangedMatcher;
+ *config.add_atom_matcher() = brightnessChangedMatcher2;
+
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(crashMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+ countMetric->mutable_dimensions_in_what()->set_field(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
+
+ auto metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(metricId);
+ auto event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(saverModeMatcher.id());
+ event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
+ event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
+ auto event_activation2 = metric_activation1->add_event_activation();
+ event_activation2->set_atom_matcher_id(screenOnMatcher.id());
+ event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
+ event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id());
+
+ return config;
+}
+
+StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
+ auto crashMatcher = CreateProcessCrashAtomMatcher();
+ auto foregroundMatcher = CreateMoveToForegroundAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher();
+ brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2"));
+
+ *config.add_atom_matcher() = saverModeMatcher;
+ *config.add_atom_matcher() = crashMatcher;
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = brightnessChangedMatcher;
+ *config.add_atom_matcher() = brightnessChangedMatcher2;
+ *config.add_atom_matcher() = foregroundMatcher;
+
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(crashMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+ countMetric->mutable_dimensions_in_what()->set_field(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
+
+ int64_t metricId2 = 234567;
+ countMetric = config.add_count_metric();
+ countMetric->set_id(metricId2);
+ countMetric->set_what(foregroundMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+ countMetric->mutable_dimensions_in_what()->set_field(
+ android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
+
+ auto metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(metricId);
+ auto event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(saverModeMatcher.id());
+ event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
+ event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
+ auto event_activation2 = metric_activation1->add_event_activation();
+ event_activation2->set_atom_matcher_id(screenOnMatcher.id());
+ event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
+ event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id());
+
+ metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(metricId2);
+ event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(saverModeMatcher.id());
+ event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
+ event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
+ event_activation2 = metric_activation1->add_event_activation();
+ event_activation2->set_atom_matcher_id(screenOnMatcher.id());
+ event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
+ event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id());
+
+ return config;
+}
+
} // namespace
TEST(MetricActivationE2eTest, TestCountMetric) {
auto config = CreateStatsdConfig();
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
int uid = 12345;
int64_t cfgId = 98765;
@@ -108,12 +244,12 @@
EXPECT_EQ(eventActivationMap.size(), 2u);
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0].activation_ns, 0);
- EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2].activation_ns, 0);
- EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
std::unique_ptr<LogEvent> event;
@@ -131,12 +267,12 @@
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);
- EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2].activation_ns, 0);
- EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
@@ -148,12 +284,12 @@
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);
- EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
@@ -161,12 +297,12 @@
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);
- EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
- 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);
+ 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);
+ 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);
@@ -182,14 +318,14 @@
// 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);
- 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);
+ 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);
+ 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);
- // Re-activate.
+ // Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
processor.OnLogEvent(event.get());
@@ -198,13 +334,14 @@
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);
- EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ // 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
processor.OnLogEvent(event.get());
@@ -272,9 +409,1192 @@
data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
data.bucket_info(0).end_bucket_elapsed_nanos());
-
}
+TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
+ auto config = CreateStatsdConfigWithOneDeactivation();
+
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ 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;
+ auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+
+ 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).
+ EXPECT_EQ(eventActivationMap.size(), 2u);
+ EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+ EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap.size(), 1u);
+ EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ std::unique_ptr<LogEvent> event;
+
+ event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
+ 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());
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ // First processed event.
+ event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
+ processor.OnLogEvent(event.get());
+
+ // Activated by screen on event.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + 20);
+ 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);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ // 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());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ 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);
+ 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);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ // 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());
+
+ // All activations expired.
+ event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+ 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);
+ 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);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ // Re-activate metric via screen on.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ // 4th processed event.
+ event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+ processor.OnLogEvent(event.get());
+
+ // Re-enable battery saver mode activation.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ 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::kActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ // 5th processed event.
+ event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+ processor.OnLogEvent(event.get());
+
+ // Cancel battery saver mode activation.
+ event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
+ 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 + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ // Screen-on activation expired.
+ event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ // New broadcast since the config is no longer active.
+ EXPECT_EQ(broadcastCount, 4);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+ processor.OnLogEvent(event.get());
+
+ // Re-enable battery saver mode activation.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 5);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ // Cancel battery saver mode activation.
+ event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 6);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ EXPECT_EQ(5, countMetrics.data_size());
+
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ // Partial bucket as metric is deactivated.
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
+ auto config = CreateStatsdConfigWithTwoDeactivations();
+
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ 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;
+ auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+
+ 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).
+ EXPECT_EQ(eventActivationMap.size(), 2u);
+ EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+ EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap.size(), 2u);
+ EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
+ EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ std::unique_ptr<LogEvent> event;
+
+ event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
+ 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());
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ // First processed event.
+ event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
+ processor.OnLogEvent(event.get());
+
+ // Activated by screen on event.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + 20);
+ 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);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ // 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());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ 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);
+ 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);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ // 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());
+
+ // All activations expired.
+ event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+ 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);
+ 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);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ // Re-activate metric via screen on.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ // 4th processed event.
+ event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+ processor.OnLogEvent(event.get());
+
+ // Re-enable battery saver mode activation.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ 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::kActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ // 5th processed event.
+ event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+ processor.OnLogEvent(event.get());
+
+ // Cancel battery saver mode and screen on activation.
+ event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ // New broadcast since the config is no longer active.
+ EXPECT_EQ(broadcastCount, 4);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ // Screen-on activation expired.
+ event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 4);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+ processor.OnLogEvent(event.get());
+
+ // Re-enable battery saver mode activation.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 5);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ // Cancel battery saver mode and screen on activation.
+ event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 6);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ EXPECT_EQ(5, countMetrics.data_size());
+
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ // Partial bucket as metric is deactivated.
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
+ auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations();
+
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ 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(), 2);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ auto& eventActivationMap = metricProducer->mEventActivationMap;
+ auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+ sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1];
+ auto& eventActivationMap2 = metricProducer2->mEventActivationMap;
+ auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap;
+
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_FALSE(metricProducer2->mIsActive);
+ // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+ // triggered by screen on event (tracker index 2).
+ EXPECT_EQ(eventActivationMap.size(), 2u);
+ EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+ EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap.size(), 2u);
+ EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
+ EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+
+ EXPECT_EQ(eventActivationMap2.size(), 2u);
+ EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end());
+ EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end());
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2.size(), 2u);
+ EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end());
+ EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end());
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ std::unique_ptr<LogEvent> event;
+
+ event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
+ processor.OnLogEvent(event.get());
+ event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_FALSE(metricProducer2->mIsActive);
+ EXPECT_EQ(broadcastCount, 0);
+
+ // Activated by battery save mode.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_EQ(broadcastCount, 1);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_TRUE(metricProducer->mIsActive);
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_TRUE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ // First processed event.
+ event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
+ processor.OnLogEvent(event.get());
+ event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15);
+ processor.OnLogEvent(event.get());
+
+ // Activated by screen on event.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + 20);
+ 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);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_TRUE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ // 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());
+ event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+ 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);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ 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);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_TRUE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+ // 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());
+ event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+ processor.OnLogEvent(event.get());
+
+ // All activations expired.
+ event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+ processor.OnLogEvent(event.get());
+ event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ // New broadcast since the config is no longer active.
+ EXPECT_EQ(broadcastCount, 2);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_FALSE(metricProducer->mIsActive);
+ 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);
+ 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);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_FALSE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ // Re-activate metric via screen on.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_EQ(broadcastCount, 3);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_TRUE(metricProducer->mIsActive);
+ 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);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_TRUE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ // 4th processed event.
+ event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+ processor.OnLogEvent(event.get());
+ event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+ processor.OnLogEvent(event.get());
+
+ // Re-enable battery saver mode activation.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_EQ(broadcastCount, 3);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_TRUE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ // 5th processed event.
+ event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+ processor.OnLogEvent(event.get());
+ event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+ processor.OnLogEvent(event.get());
+
+ // Cancel battery saver mode and screen on activation.
+ event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ // New broadcast since the config is no longer active.
+ EXPECT_EQ(broadcastCount, 4);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_FALSE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ // Screen-on activation expired.
+ event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+ processor.OnLogEvent(event.get());
+ event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_EQ(broadcastCount, 4);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_FALSE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+ processor.OnLogEvent(event.get());
+ event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+ processor.OnLogEvent(event.get());
+
+ // Re-enable battery saver mode activation.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_EQ(broadcastCount, 5);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_TRUE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ // Cancel battery saver mode and screen on activation.
+ event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_EQ(broadcastCount, 6);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_FALSE(metricProducer2->mIsActive);
+ EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+ EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(2, reports.reports(0).metrics_size());
+ EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+ EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size());
+
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ EXPECT_EQ(5, countMetrics.data_size());
+
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ // Partial bucket as metric is deactivated.
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+
+ countMetrics.clear_data();
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(1).count_metrics(), &countMetrics);
+ EXPECT_EQ(5, countMetrics.data_size());
+
+ data = countMetrics.data(0);
+ EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ // Partial bucket as metric is deactivated.
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 80b6349..395c867 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -513,71 +513,75 @@
/** @hide Process is hosting a foreground service with location type. */
public static final int PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3;
+ /** @hide Process is bound to a TOP app. This is ranked below SERVICE_LOCATION so that
+ * it doesn't get the capability of location access while-in-use. */
+ public static final int PROCESS_STATE_BOUND_TOP = 4;
+
/** @hide Process is hosting a foreground service. */
@UnsupportedAppUsage
- public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
+ public static final int PROCESS_STATE_FOREGROUND_SERVICE = 5;
/** @hide Process is hosting a foreground service due to a system binding. */
@UnsupportedAppUsage
- public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5;
+ public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6;
/** @hide Process is important to the user, and something they are aware of. */
- public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
+ public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 7;
/** @hide Process is important to the user, but not something they are aware of. */
@UnsupportedAppUsage
- public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
+ public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 8;
/** @hide Process is in the background transient so we will try to keep running. */
- public static final int PROCESS_STATE_TRANSIENT_BACKGROUND = 8;
+ public static final int PROCESS_STATE_TRANSIENT_BACKGROUND = 9;
/** @hide Process is in the background running a backup/restore operation. */
- public static final int PROCESS_STATE_BACKUP = 9;
+ public static final int PROCESS_STATE_BACKUP = 10;
/** @hide Process is in the background running a service. Unlike oom_adj, this level
* is used for both the normal running in background state and the executing
* operations state. */
@UnsupportedAppUsage
- public static final int PROCESS_STATE_SERVICE = 10;
+ public static final int PROCESS_STATE_SERVICE = 11;
/** @hide Process is in the background running a receiver. Note that from the
* perspective of oom_adj, receivers run at a higher foreground level, but for our
* prioritization here that is not necessary and putting them below services means
* many fewer changes in some process states as they receive broadcasts. */
@UnsupportedAppUsage
- public static final int PROCESS_STATE_RECEIVER = 11;
+ public static final int PROCESS_STATE_RECEIVER = 12;
/** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */
- public static final int PROCESS_STATE_TOP_SLEEPING = 12;
+ public static final int PROCESS_STATE_TOP_SLEEPING = 13;
/** @hide Process is in the background, but it can't restore its state so we want
* to try to avoid killing it. */
- public static final int PROCESS_STATE_HEAVY_WEIGHT = 13;
+ public static final int PROCESS_STATE_HEAVY_WEIGHT = 14;
/** @hide Process is in the background but hosts the home activity. */
@UnsupportedAppUsage
- public static final int PROCESS_STATE_HOME = 14;
+ public static final int PROCESS_STATE_HOME = 15;
/** @hide Process is in the background but hosts the last shown activity. */
- public static final int PROCESS_STATE_LAST_ACTIVITY = 15;
+ public static final int PROCESS_STATE_LAST_ACTIVITY = 16;
/** @hide Process is being cached for later use and contains activities. */
@UnsupportedAppUsage
- public static final int PROCESS_STATE_CACHED_ACTIVITY = 16;
+ public static final int PROCESS_STATE_CACHED_ACTIVITY = 17;
/** @hide Process is being cached for later use and is a client of another cached
* process that contains activities. */
- public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17;
+ public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18;
/** @hide Process is being cached for later use and has an activity that corresponds
* to an existing recent task. */
- public static final int PROCESS_STATE_CACHED_RECENT = 18;
+ public static final int PROCESS_STATE_CACHED_RECENT = 19;
/** @hide Process is being cached for later use and is empty. */
- public static final int PROCESS_STATE_CACHED_EMPTY = 19;
+ public static final int PROCESS_STATE_CACHED_EMPTY = 20;
/** @hide Process does not exist. */
- public static final int PROCESS_STATE_NONEXISTENT = 20;
+ public static final int PROCESS_STATE_NONEXISTENT = 21;
// NOTE: If PROCESS_STATEs are added, then new fields must be added
// to frameworks/base/core/proto/android/app/enums.proto and the following method must
@@ -602,6 +606,8 @@
return AppProtoEnums.PROCESS_STATE_PERSISTENT_UI;
case PROCESS_STATE_TOP:
return AppProtoEnums.PROCESS_STATE_TOP;
+ case PROCESS_STATE_BOUND_TOP:
+ return AppProtoEnums.PROCESS_STATE_BOUND_TOP;
case PROCESS_STATE_FOREGROUND_SERVICE_LOCATION:
case PROCESS_STATE_FOREGROUND_SERVICE:
return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 7eab5db..4ef554d 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -19,6 +19,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -146,6 +147,7 @@
return IActivityTaskManagerSingleton.get();
}
+ @UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
new Singleton<IActivityTaskManager>() {
@Override
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b2b1e77..38006dc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5920,6 +5920,10 @@
UserHandle.myUserId());
VMRuntime.setProcessPackageName(data.appInfo.packageName);
+ // Pass data directory path to ART. This is used for caching information and
+ // should be set before any application code is loaded.
+ VMRuntime.setProcessDataDirectory(data.appInfo.dataDir);
+
if (mProfiler.profileFd != null) {
mProfiler.startProfiling();
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8a52265..83c5e20 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2147,8 +2147,8 @@
public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2;
/**
- * Represents the update file being wrong, i.e. payloads are mismatched, wrong compressions
- * method.
+ * Represents the update file being wrong; e.g. payloads are mismatched, or the wrong
+ * compression method is used.
*/
public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d7a2e1b..af738da 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -427,7 +427,7 @@
* invisible background activities. This will impact the number of
* recent activities the user can switch between without having them
* restart. There is no guarantee this will be respected, as the system
- * tries to balance such requests from one app vs. the importantance of
+ * tries to balance such requests from one app vs. the importance of
* keeping other apps around.
*/
public static final int BIND_VISIBLE = 0x10000000;
diff --git a/core/java/android/content/LoggingContentInterface.java b/core/java/android/content/LoggingContentInterface.java
index 6e12a57..83c0c91 100644
--- a/core/java/android/content/LoggingContentInterface.java
+++ b/core/java/android/content/LoggingContentInterface.java
@@ -48,191 +48,197 @@
this.delegate = delegate;
}
- private void log(String method, Object res, Object... args) {
- // First, force-unparcel any bundles so we can log them
- for (Object arg : args) {
- if (arg instanceof Bundle) {
- ((Bundle) arg).size();
+ private class Logger implements AutoCloseable {
+ private final StringBuilder sb = new StringBuilder();
+
+ public Logger(String method, Object... args) {
+ // First, force-unparcel any bundles so we can log them
+ for (Object arg : args) {
+ if (arg instanceof Bundle) {
+ ((Bundle) arg).size();
+ }
+ }
+
+ sb.append("callingUid=").append(Binder.getCallingUid()).append(' ');
+ sb.append(method);
+ sb.append('(').append(deepToString(args)).append(')');
+ }
+
+ private String deepToString(Object value) {
+ if (value != null && value.getClass().isArray()) {
+ return Arrays.deepToString((Object[]) value);
+ } else {
+ return String.valueOf(value);
}
}
- final StringBuilder sb = new StringBuilder();
- sb.append("callingUid=").append(Binder.getCallingUid()).append(' ');
- sb.append(method);
- sb.append('(').append(deepToString(args)).append(')');
- if (res instanceof Cursor) {
- sb.append('\n');
- DatabaseUtils.dumpCursor((Cursor) res, sb);
- } else {
- sb.append(" = ").append(deepToString(res));
+ public <T> T setResult(T res) {
+ if (res instanceof Cursor) {
+ sb.append('\n');
+ DatabaseUtils.dumpCursor((Cursor) res, sb);
+ } else {
+ sb.append(" = ").append(deepToString(res));
+ }
+ return res;
}
- if (res instanceof Exception) {
- Log.e(tag, sb.toString());
- } else {
+ @Override
+ public void close() {
Log.v(tag, sb.toString());
}
}
- private String deepToString(Object value) {
- if (value != null && value.getClass().isArray()) {
- return Arrays.deepToString((Object[]) value);
- } else {
- return String.valueOf(value);
- }
- }
-
@Override
public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
throws RemoteException {
- try {
- final Cursor res = delegate.query(uri, projection, queryArgs, cancellationSignal);
- log("query", res, uri, projection, queryArgs, cancellationSignal);
- return res;
- } catch (Exception res) {
- log("query", res, uri, projection, queryArgs, cancellationSignal);
- throw res;
+ try (Logger l = new Logger("query", uri, projection, queryArgs, cancellationSignal)) {
+ try {
+ return l.setResult(delegate.query(uri, projection, queryArgs, cancellationSignal));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable String getType(@NonNull Uri uri) throws RemoteException {
- try {
- final String res = delegate.getType(uri);
- log("getType", res, uri);
- return res;
- } catch (Exception res) {
- log("getType", res, uri);
- throw res;
+ try (Logger l = new Logger("getType", uri)) {
+ try {
+ return l.setResult(delegate.getType(uri));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter)
throws RemoteException {
- try {
- final String[] res = delegate.getStreamTypes(uri, mimeTypeFilter);
- log("getStreamTypes", res, uri, mimeTypeFilter);
- return res;
- } catch (Exception res) {
- log("getStreamTypes", res, uri, mimeTypeFilter);
- throw res;
+ try (Logger l = new Logger("getStreamTypes", uri, mimeTypeFilter)) {
+ try {
+ return l.setResult(delegate.getStreamTypes(uri, mimeTypeFilter));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable Uri canonicalize(@NonNull Uri uri) throws RemoteException {
- try {
- final Uri res = delegate.canonicalize(uri);
- log("canonicalize", res, uri);
- return res;
- } catch (Exception res) {
- log("canonicalize", res, uri);
- throw res;
+ try (Logger l = new Logger("canonicalize", uri)) {
+ try {
+ return l.setResult(delegate.canonicalize(uri));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable Uri uncanonicalize(@NonNull Uri uri) throws RemoteException {
- try {
- final Uri res = delegate.uncanonicalize(uri);
- log("uncanonicalize", res, uri);
- return res;
- } catch (Exception res) {
- log("uncanonicalize", res, uri);
- throw res;
+ try (Logger l = new Logger("uncanonicalize", uri)) {
+ try {
+ return l.setResult(delegate.uncanonicalize(uri));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public boolean refresh(@NonNull Uri uri, @Nullable Bundle args,
@Nullable CancellationSignal cancellationSignal) throws RemoteException {
- try {
- final boolean res = delegate.refresh(uri, args, cancellationSignal);
- log("refresh", res, uri, args, cancellationSignal);
- return res;
- } catch (Exception res) {
- log("refresh", res, uri, args, cancellationSignal);
- throw res;
+ try (Logger l = new Logger("refresh", uri, args, cancellationSignal)) {
+ try {
+ return l.setResult(delegate.refresh(uri, args, cancellationSignal));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues)
throws RemoteException {
- try {
- final Uri res = delegate.insert(uri, initialValues);
- log("insert", res, uri, initialValues);
- return res;
- } catch (Exception res) {
- log("insert", res, uri, initialValues);
- throw res;
+ try (Logger l = new Logger("insert", uri, initialValues)) {
+ try {
+ return l.setResult(delegate.insert(uri, initialValues));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] initialValues)
throws RemoteException {
- try {
- final int res = delegate.bulkInsert(uri, initialValues);
- log("bulkInsert", res, uri, initialValues);
- return res;
- } catch (Exception res) {
- log("bulkInsert", res, uri, initialValues);
- throw res;
+ try (Logger l = new Logger("bulkInsert", uri, initialValues)) {
+ try {
+ return l.setResult(delegate.bulkInsert(uri, initialValues));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection,
@Nullable String[] selectionArgs) throws RemoteException {
- try {
- final int res = delegate.delete(uri, selection, selectionArgs);
- log("delete", res, uri, selection, selectionArgs);
- return res;
- } catch (Exception res) {
- log("delete", res, uri, selection, selectionArgs);
- throw res;
+ try (Logger l = new Logger("delete", uri, selection, selectionArgs)) {
+ try {
+ return l.setResult(delegate.delete(uri, selection, selectionArgs));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) throws RemoteException {
- try {
- final int res = delegate.update(uri, values, selection, selectionArgs);
- log("update", res, uri, values, selection, selectionArgs);
- return res;
- } catch (Exception res) {
- log("update", res, uri, values, selection, selectionArgs);
- throw res;
+ try (Logger l = new Logger("update", uri, values, selection, selectionArgs)) {
+ try {
+ return l.setResult(delegate.update(uri, values, selection, selectionArgs));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
@Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
- try {
- final ParcelFileDescriptor res = delegate.openFile(uri, mode, signal);
- log("openFile", res, uri, mode, signal);
- return res;
- } catch (Exception res) {
- log("openFile", res, uri, mode, signal);
- throw res;
+ try (Logger l = new Logger("openFile", uri, mode, signal)) {
+ try {
+ return l.setResult(delegate.openFile(uri, mode, signal));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
@Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
- try {
- final AssetFileDescriptor res = delegate.openAssetFile(uri, mode, signal);
- log("openAssetFile", res, uri, mode, signal);
- return res;
- } catch (Exception res) {
- log("openAssetFile", res, uri, mode, signal);
- throw res;
+ try (Logger l = new Logger("openAssetFile", uri, mode, signal)) {
+ try {
+ return l.setResult(delegate.openAssetFile(uri, mode, signal));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@@ -240,13 +246,13 @@
public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
@NonNull String mimeTypeFilter, @Nullable Bundle opts,
@Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
- try {
- final AssetFileDescriptor res = delegate.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
- log("openTypedAssetFile", res, uri, mimeTypeFilter, opts, signal);
- return res;
- } catch (Exception res) {
- log("openTypedAssetFile", res, uri, mimeTypeFilter, opts, signal);
- throw res;
+ try (Logger l = new Logger("openTypedAssetFile", uri, mimeTypeFilter, opts, signal)) {
+ try {
+ return l.setResult(delegate.openTypedAssetFile(uri, mimeTypeFilter, opts, signal));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@@ -254,26 +260,26 @@
public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
@NonNull ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
- try {
- final ContentProviderResult[] res = delegate.applyBatch(authority, operations);
- log("applyBatch", res, authority, operations);
- return res;
- } catch (Exception res) {
- log("applyBatch", res, authority, operations);
- throw res;
+ try (Logger l = new Logger("applyBatch", authority, operations)) {
+ try {
+ return l.setResult(delegate.applyBatch(authority, operations));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
@Override
public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
@Nullable String arg, @Nullable Bundle extras) throws RemoteException {
- try {
- final Bundle res = delegate.call(authority, method, arg, extras);
- log("call", res, authority, method, arg, extras);
- return res;
- } catch (Exception res) {
- log("call", res, authority, method, arg, extras);
- throw res;
+ try (Logger l = new Logger("call", authority, method, arg, extras)) {
+ try {
+ return l.setResult(delegate.call(authority, method, arg, extras));
+ } catch (Exception res) {
+ l.setResult(res);
+ throw res;
+ }
}
}
}
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 91424f4..fc79a42 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -255,7 +255,7 @@
* @hide
*/
@SystemApi
- @Nullable
+ @NonNull
public String getTargetPackageName() {
return targetPackageName;
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 5328dda..1c2afd2 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -687,6 +687,13 @@
*/
public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29;
+ /**
+ * Value for {@link #privateFlags}: whether this app is pre-installed on the
+ * ODM partition of the system image.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_ODM = 1 << 30;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -717,6 +724,7 @@
PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE,
PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX,
+ PRIVATE_FLAG_ODM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlags {}
@@ -1970,6 +1978,11 @@
}
/** @hide */
+ public boolean isOdm() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+ }
+
+ /** @hide */
public boolean isPartiallyDirectBootAware() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 93bc6d7..81788b9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6920,6 +6920,11 @@
}
/** @hide */
+ public boolean isOdm() {
+ return applicationInfo.isOdm();
+ }
+
+ /** @hide */
public boolean isPrivileged() {
return applicationInfo.isPrivilegedApp();
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 514015f..e5ef67b 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.StyleRes;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
@@ -357,6 +358,22 @@
return sEmptyApkAssets;
}
+ /** @hide */
+ @TestApi
+ public @NonNull String[] getApkPaths() {
+ synchronized (this) {
+ if (mOpen) {
+ String[] paths = new String[mApkAssets.length];
+ final int count = mApkAssets.length;
+ for (int i = 0; i < count; i++) {
+ paths[i] = mApkAssets[i].getAssetPath();
+ }
+ return paths;
+ }
+ }
+ return new String[0];
+ }
+
/**
* Returns a cookie for use with the other APIs of AssetManager.
* @return 0 if the path was not found, otherwise a positive integer cookie representing
@@ -1356,6 +1373,7 @@
/**
* @hide
*/
+ @TestApi
@GuardedBy("this")
public @Nullable Map<String, String> getOverlayableMap(String packageName) {
synchronized (this) {
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 44bd883..647448c 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -61,7 +61,10 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
+ // May throw CursorWindowAllocationException
private static native long nativeCreate(String name, int cursorWindowSize);
+
+ // May throw CursorWindowAllocationException
private static native long nativeCreateFromParcel(Parcel parcel);
private static native void nativeDispose(long windowPtr);
private static native void nativeWriteToParcel(long windowPtr, Parcel parcel);
@@ -135,8 +138,7 @@
mName = name != null && name.length() != 0 ? name : "<unnamed>";
mWindowPtr = nativeCreate(mName, (int) windowSizeBytes);
if (mWindowPtr == 0) {
- throw new CursorWindowAllocationException("Cursor window allocation of " +
- windowSizeBytes + " bytes failed. " + printStats());
+ throw new IllegalStateException(); // Shouldn't happen.
}
mCloseGuard.open("close");
recordNewWindow(Binder.getCallingPid(), mWindowPtr);
@@ -164,8 +166,7 @@
mStartPos = source.readInt();
mWindowPtr = nativeCreateFromParcel(source);
if (mWindowPtr == 0) {
- throw new CursorWindowAllocationException("Cursor window could not be "
- + "created from binder.");
+ throw new IllegalStateException(); // Shouldn't happen.
}
mName = nativeGetName(mWindowPtr);
mCloseGuard.open("close");
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 2923bbf..0daf30f25 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -16,6 +16,7 @@
package android.hardware.input;
+import android.graphics.Rect;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.IInputDevicesChangedListener;
@@ -24,6 +25,7 @@
import android.os.IBinder;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputMonitor;
import android.view.PointerIcon;
/** @hide */
@@ -82,4 +84,7 @@
void setCustomPointerIcon(in PointerIcon icon);
void requestPointerCapture(IBinder windowToken, boolean enabled);
+
+ /** Create an input monitor for gestures. */
+ InputMonitor monitorGestureInput(String name, int displayId);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index fec5c34..2a59be2 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -41,6 +41,7 @@
import android.util.SparseArray;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.PointerIcon;
@@ -933,6 +934,19 @@
}
}
+ /**
+ * Monitor input on the specified display for gestures.
+ *
+ * @hide
+ */
+ public InputMonitor monitorGestureInput(String name, int displayId) {
+ try {
+ return mIm.monitorGestureInput(name, displayId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private void populateInputDevicesLocked() {
if (mInputDevicesChangedListener == null) {
final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index cf3395a..5b5bd76 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -25,6 +25,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityThread;
import android.media.AudioFormat;
import android.os.Handler;
import android.os.Parcel;
@@ -1440,6 +1441,17 @@
public static final int SERVICE_STATE_DISABLED = 1;
/**
+ * @return returns current package name.
+ */
+ static String getCurrentOpPackageName() {
+ String packageName = ActivityThread.currentOpPackageName();
+ if (packageName == null) {
+ return "";
+ }
+ return packageName;
+ }
+
+ /**
* Returns a list of descriptors for all hardware modules loaded.
* @param modules A ModuleProperties array where the list will be returned.
* @return - {@link #STATUS_OK} in case of success
@@ -1452,7 +1464,23 @@
* @hide
*/
@UnsupportedAppUsage
- public static native int listModules(ArrayList <ModuleProperties> modules);
+ public static int listModules(ArrayList<ModuleProperties> modules) {
+ return listModules(getCurrentOpPackageName(), modules);
+ }
+
+ /**
+ * Returns a list of descriptors for all hardware modules loaded.
+ * @param opPackageName
+ * @param modules A ModuleProperties array where the list will be returned.
+ * @return - {@link #STATUS_OK} in case of success
+ * - {@link #STATUS_ERROR} in case of unspecified error
+ * - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
+ * - {@link #STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link #STATUS_BAD_VALUE} if modules is null
+ * - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+ */
+ private static native int listModules(String opPackageName,
+ ArrayList<ModuleProperties> modules);
/**
* Get an interface on a hardware module to control sound models and recognition on
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 402c228..9113548 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -46,9 +46,10 @@
SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) {
mId = moduleId;
mEventHandlerDelegate = new NativeEventHandlerDelegate(listener, handler);
- native_setup(new WeakReference<SoundTriggerModule>(this));
+ native_setup(SoundTrigger.getCurrentOpPackageName(),
+ new WeakReference<SoundTriggerModule>(this));
}
- private native void native_setup(Object module_this);
+ private native void native_setup(String opPackageName, Object moduleThis);
@Override
protected void finalize() {
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index 1339432..a66fcae 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -137,6 +137,8 @@
/**
* Log a captive portal login event.
+ * @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto.
+ * @param packageName captive portal application package name.
* @hide
*/
@SystemApi
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6ba4a30..2906710 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2617,7 +2617,7 @@
/**
* Start listening to tethering change events. Any new added callback will receive the last
- * tethering status right away. If callback is registered when tethering loses its upstream or
+ * tethering status right away. If callback is registered when tethering has no upstream or
* disabled, {@link OnTetheringEventCallback#onUpstreamChanged} will immediately be called
* with a null argument. The same callback object cannot be registered twice.
*
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 1a3a6f8..2e52f72 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -72,15 +72,12 @@
* Create a {@link StaticIpConfiguration} based on the DhcpResults.
*/
public StaticIpConfiguration toStaticIpConfiguration() {
- final StaticIpConfiguration s = new StaticIpConfiguration();
- // All these except dnsServers are immutable, so no need to make copies.
- s.setIpAddress(ipAddress);
- s.setGateway(gateway);
- for (InetAddress addr : dnsServers) {
- s.addDnsServer(addr);
- }
- s.setDomains(domains);
- return s;
+ return new StaticIpConfiguration.Builder()
+ .setIpAddress(ipAddress)
+ .setGateway(gateway)
+ .setDnsServers(dnsServers)
+ .setDomains(domains)
+ .build();
}
public DhcpResults(StaticIpConfiguration source) {
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 565f36f..87f8739 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -22,6 +22,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.net.shared.InetAddressUtils;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,20 +34,19 @@
/**
* Class that describes static IP configuration.
*
- * This class is different from LinkProperties because it represents
+ * <p>This class is different from {@link LinkProperties} because it represents
* configuration intent. The general contract is that if we can represent
* a configuration here, then we should be able to configure it on a network.
* The intent is that it closely match the UI we have for configuring networks.
*
- * In contrast, LinkProperties represents current state. It is much more
+ * <p>In contrast, {@link LinkProperties} represents current state. It is much more
* expressive. For example, it supports multiple IP addresses, multiple routes,
* stacked interfaces, and so on. Because LinkProperties is so expressive,
* using it to represent configuration intent as well as current state causes
* problems. For example, we could unknowingly save a configuration that we are
* not in fact capable of applying, or we could save a configuration that the
* UI cannot display, which has the potential for malicious code to hide
- * hostile or unexpected configuration from the user: see, for example,
- * http://b/12663469 and http://b/16893413 .
+ * hostile or unexpected configuration from the user.
*
* @hide
*/
@@ -54,24 +54,24 @@
@TestApi
public final class StaticIpConfiguration implements Parcelable {
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@Nullable
public LinkAddress ipAddress;
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@Nullable
public InetAddress gateway;
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@NonNull
public final ArrayList<InetAddress> dnsServers;
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@Nullable
public String domains;
public StaticIpConfiguration() {
- dnsServers = new ArrayList<InetAddress>();
+ dnsServers = new ArrayList<>();
}
public StaticIpConfiguration(@Nullable StaticIpConfiguration source) {
@@ -92,32 +92,96 @@
domains = null;
}
+ /**
+ * Get the static IP address included in the configuration.
+ */
public @Nullable LinkAddress getIpAddress() {
return ipAddress;
}
- public void setIpAddress(@Nullable LinkAddress ipAddress) {
- this.ipAddress = ipAddress;
- }
-
+ /**
+ * Get the gateway included in the configuration.
+ */
public @Nullable InetAddress getGateway() {
return gateway;
}
- public void setGateway(@Nullable InetAddress gateway) {
- this.gateway = gateway;
- }
-
+ /**
+ * Get the DNS servers included in the configuration.
+ */
public @NonNull List<InetAddress> getDnsServers() {
return dnsServers;
}
+ /**
+ * Get a {@link String} listing in priority order of the comma separated domains to search when
+ * resolving host names on the link.
+ */
public @Nullable String getDomains() {
return domains;
}
- public void setDomains(@Nullable String newDomains) {
- domains = newDomains;
+ /**
+ * Helper class to build a new instance of {@link StaticIpConfiguration}.
+ */
+ public static final class Builder {
+ private LinkAddress mIpAddress;
+ private InetAddress mGateway;
+ private Iterable<InetAddress> mDnsServers;
+ private String mDomains;
+
+ /**
+ * Set the IP address to be included in the configuration; null by default.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setIpAddress(@Nullable LinkAddress ipAddress) {
+ mIpAddress = ipAddress;
+ return this;
+ }
+
+ /**
+ * Set the address of the gateway to be included in the configuration; null by default.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setGateway(@Nullable InetAddress gateway) {
+ mGateway = gateway;
+ return this;
+ }
+
+ /**
+ * Set the addresses of the DNS servers included in the configuration; empty by default.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setDnsServers(@NonNull Iterable<InetAddress> dnsServers) {
+ mDnsServers = dnsServers;
+ return this;
+ }
+
+ /**
+ * Sets the DNS domain search path to be used on the link; null by default.
+ * @param newDomains A {@link String} containing the comma separated domains to search when
+ * resolving host names on this link, in priority order.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setDomains(@Nullable String newDomains) {
+ mDomains = newDomains;
+ return this;
+ }
+
+ /**
+ * Create a {@link StaticIpConfiguration} from the parameters in this {@link Builder}.
+ * @return The newly created StaticIpConfiguration.
+ */
+ public @NonNull StaticIpConfiguration build() {
+ final StaticIpConfiguration config = new StaticIpConfiguration();
+ config.ipAddress = mIpAddress;
+ config.gateway = mGateway;
+ for (InetAddress server : mDnsServers) {
+ config.dnsServers.add(server);
+ }
+ config.domains = mDomains;
+ return config;
+ }
}
/**
@@ -129,16 +193,17 @@
/**
* Returns the network routes specified by this object. Will typically include a
- * directly-connected route for the IP address's local subnet and a default route. If the
- * default gateway is not covered by the directly-connected route, it will also contain a host
- * route to the gateway as well. This configuration is arguably invalid, but it used to work
- * in K and earlier, and other OSes appear to accept it.
+ * directly-connected route for the IP address's local subnet and a default route.
+ * @param iface Interface to include in the routes.
*/
public @NonNull List<RouteInfo> getRoutes(@Nullable String iface) {
List<RouteInfo> routes = new ArrayList<RouteInfo>(3);
if (ipAddress != null) {
RouteInfo connectedRoute = new RouteInfo(ipAddress, null, iface);
routes.add(connectedRoute);
+ // If the default gateway is not covered by the directly-connected route, also add a
+ // host route to the gateway as well. This configuration is arguably invalid, but it
+ // used to work in K and earlier, and other OSes appear to accept it.
if (gateway != null && !connectedRoute.matches(gateway)) {
routes.add(RouteInfo.makeHostRoute(gateway, iface));
}
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index f23dc2d..c13f64e 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -34,6 +34,7 @@
import java.util.HashMap;
import java.util.List;
+import java.util.regex.Pattern;
/**
* This class can be used to query the state of
@@ -48,6 +49,7 @@
* on the device.
*/
public final class CardEmulation {
+ private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
static final String TAG = "CardEmulation";
/**
@@ -732,7 +734,7 @@
}
// Verify hex characters
- if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?\\#?")) {
+ if (!AID_PATTERN.matcher(aid).matches()) {
Log.e(TAG, "AID " + aid + " is not a valid AID.");
return false;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b64fe00..00d522b 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -16,6 +16,7 @@
package android.os;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION;
import android.annotation.UnsupportedAppUsage;
@@ -859,7 +860,8 @@
*/
public static final int[] CRITICAL_PROC_STATES = {
PROCESS_STATE_TOP,
- PROCESS_STATE_FOREGROUND_SERVICE_LOCATION, PROCESS_STATE_FOREGROUND_SERVICE,
+ PROCESS_STATE_FOREGROUND_SERVICE_LOCATION,
+ PROCESS_STATE_BOUND_TOP, PROCESS_STATE_FOREGROUND_SERVICE,
PROCESS_STATE_FOREGROUND
};
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index eb91860..66ddf21 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -444,24 +444,20 @@
*
* @param workSource The original UID responsible for the binder call.
* @return token to restore original work source.
- * @hide
**/
@CriticalNative
- @SystemApi
public static final native long setCallingWorkSourceUid(int workSource);
/**
* Returns the work source set by the caller.
*
* Unlike {@link Binder#getCallingUid()}, this result of this method cannot be trusted. The
- * caller can set the value to whatever he wants. Only use this value if you trust the calling
+ * caller can set the value to whatever they want. Only use this value if you trust the calling
* uid.
*
* @return The original UID responsible for the binder transaction.
- * @hide
*/
@CriticalNative
- @SystemApi
public static final native int getCallingWorkSourceUid();
/**
@@ -484,10 +480,8 @@
* </pre>
*
* @return token to restore original work source.
- * @hide
**/
@CriticalNative
- @SystemApi
public static final native long clearCallingWorkSource();
/**
@@ -503,11 +497,8 @@
* Binder.restoreCallingWorkSource(token);
* }
* </pre>
- *
- * @hide
**/
@CriticalNative
- @SystemApi
public static final native void restoreCallingWorkSource(long token);
/**
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index 8ffafe4..f007dff 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -39,6 +39,13 @@
private static final int SELINUX_ANDROID_RESTORECON_DATADATA = 16;
/**
+ * Get context associated with path by file_contexts.
+ * @param path path to the regular file to get the security context for.
+ * @return a String representing the security context or null on failure.
+ */
+ public static final native String fileSelabelLookup(String path);
+
+ /**
* Determine whether SELinux is disabled or enabled.
* @return a boolean indicating whether SELinux is enabled.
*/
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index fa2c480..8f68723 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -35,6 +35,8 @@
import android.os.Messenger;
import android.os.ParcelableException;
import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.FeatureFlagUtils;
import android.util.Slog;
import java.lang.annotation.Retention;
@@ -315,6 +317,11 @@
*/
@RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
public void bind() {
+ if (!featureFlagEnabled()) {
+ Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted.");
+ return;
+ }
+
Intent intent = new Intent();
intent.setClassName("com.android.dynsystem",
"com.android.dynsystem.DynamicSystemInstallationService");
@@ -381,6 +388,11 @@
@RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
public void start(@NonNull Uri systemUrl, @BytesLong long systemSize,
@BytesLong long userdataSize) {
+ if (!featureFlagEnabled()) {
+ Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; start() aborted.");
+ return;
+ }
+
Intent intent = new Intent();
intent.setClassName("com.android.dynsystem",
@@ -395,6 +407,11 @@
mContext.startActivity(intent);
}
+ private boolean featureFlagEnabled() {
+ return SystemProperties.getBoolean(
+ FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
+ }
+
private void handleMessage(Message msg) {
switch (msg.what) {
case MSG_POST_STATUS:
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 0458c2a..cec1945 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -159,6 +159,16 @@
}
}
+ /** @return {@code true} if the device has a dynamic system enabled */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+ public boolean isEnabled() {
+ try {
+ return mService.isEnabled();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
/**
* Remove DynamicSystem installation if present
*
@@ -174,14 +184,13 @@
}
/**
- * Enable DynamicSystem when it's not enabled, otherwise, disable it.
- *
+ * Enable or disable DynamicSystem.
* @return {@code true} if the call succeeds. {@code false} if there is no installed image.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public boolean toggle() {
+ public boolean setEnable(boolean enable) {
try {
- return mService.toggle();
+ return mService.setEnable(enable);
} catch (RemoteException e) {
throw new RuntimeException(e.toString());
}
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index 15f5b68..a34daca 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -58,6 +58,11 @@
boolean isInstalled();
/**
+ * @return true if the device has an DynamicSystem image enabled
+ */
+ boolean isEnabled();
+
+ /**
* Remove DynamicSystem installation if present
*
* @return true if the call succeeds
@@ -65,11 +70,11 @@
boolean remove();
/**
- * Enable DynamicSystem when it's not enabled, otherwise, disable it.
+ * Enable or disable DynamicSystem.
*
* @return true if the call succeeds
*/
- boolean toggle();
+ boolean setEnable(boolean enable);
/**
* Write a chunk of the DynamicSystem system image
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 9085fa2..847b8e4 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -27,6 +27,8 @@
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
+import android.media.audiopolicy.AudioProductStrategies;
+import android.media.audiopolicy.AudioVolumeGroups;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
@@ -41,6 +43,7 @@
import android.widget.SeekBar.OnSeekBarChangeListener;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.SomeArgs;
/**
* Turns a {@link SeekBar} into a volume control.
@@ -62,6 +65,26 @@
void onMuted(boolean muted, boolean zenMuted);
}
+ private static final int MSG_GROUP_VOLUME_CHANGED = 1;
+ private final Handler mVolumeHandler = new VolumeHandler();
+ private final AudioProductStrategies mAudioProductStrategies;
+ private AudioAttributes mAttributes;
+ private int mVolumeGroupId;
+
+ private final AudioManager.VolumeGroupCallback mVolumeGroupCallback =
+ new AudioManager.VolumeGroupCallback() {
+ @Override
+ public void onAudioVolumeGroupChanged(int group, int flags) {
+ if (mHandler == null) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = group;
+ args.arg2 = flags;
+ mVolumeHandler.sendMessage(mHandler.obtainMessage(MSG_GROUP_VOLUME_CHANGED, args));
+ }
+ };
+
@UnsupportedAppUsage
private final Context mContext;
private final H mUiHandler = new H();
@@ -137,6 +160,15 @@
mRingerMode = mAudioManager.getRingerModeInternal();
}
mZenMode = mNotificationManager.getZenMode();
+
+ mAudioProductStrategies = mAudioManager.getAudioProductStrategies();
+ if (mAudioProductStrategies.size() > 0) {
+ mVolumeGroupId = mAudioProductStrategies.getVolumeGroupIdForLegacyStreamType(
+ mStreamType);
+ mAttributes = mAudioProductStrategies.getAudioAttributesForLegacyStreamType(
+ mStreamType);
+ }
+
mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
mCallback = callback;
mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
@@ -297,6 +329,9 @@
postStopSample();
mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
mReceiver.setListening(false);
+ if (mAudioProductStrategies.size() > 0) {
+ unregisterVolumeGroupCb();
+ }
mSeekBar.setOnSeekBarChangeListener(null);
mHandler.getLooper().quitSafely();
mHandler = null;
@@ -314,6 +349,9 @@
System.getUriFor(System.VOLUME_SETTINGS_INT[mStreamType]),
false, mVolumeObserver);
mReceiver.setListening(true);
+ if (mAudioProductStrategies.size() > 0) {
+ registerVolumeGroupCb();
+ }
}
public void revertVolume() {
@@ -469,7 +507,9 @@
if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
- updateVolumeSlider(streamType, streamValue);
+ if (mAudioProductStrategies.size() == 0) {
+ updateVolumeSlider(streamType, streamValue);
+ }
} else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
@@ -479,8 +519,18 @@
}
} else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
- int streamVolume = mAudioManager.getStreamVolume(streamType);
- updateVolumeSlider(streamType, streamVolume);
+ if (mAudioProductStrategies.size() == 0) {
+ int streamVolume = mAudioManager.getStreamVolume(streamType);
+ updateVolumeSlider(streamType, streamVolume);
+ } else {
+ int volumeGroup = mAudioProductStrategies.getVolumeGroupIdForLegacyStreamType(
+ streamType);
+ if (volumeGroup != AudioVolumeGroups.DEFAULT_VOLUME_GROUP
+ && volumeGroup == mVolumeGroupId) {
+ int streamVolume = mAudioManager.getStreamVolume(streamType);
+ updateVolumeSlider(streamType, streamVolume);
+ }
+ }
} else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
mZenMode = mNotificationManager.getZenMode();
updateSlider();
@@ -506,4 +556,34 @@
}
}
}
+
+ private void registerVolumeGroupCb() {
+ if (mVolumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
+ mAudioManager.registerVolumeGroupCallback(Runnable::run, mVolumeGroupCallback);
+ mLastProgress = mAudioManager.getVolumeIndexForAttributes(mAttributes);
+ }
+ }
+
+ private void unregisterVolumeGroupCb() {
+ if (mVolumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
+ mAudioManager.unregisterVolumeGroupCallback(mVolumeGroupCallback);
+ }
+ }
+
+ private class VolumeHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ SomeArgs args = (SomeArgs) msg.obj;
+ switch (msg.what) {
+ case MSG_GROUP_VOLUME_CHANGED:
+ int group = (int) args.arg1;
+ if (mVolumeGroupId != group
+ || mVolumeGroupId == AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
+ return;
+ }
+ updateSlider();
+ break;
+ }
+ }
+ }
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 44adc1c..ef28f07 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -35,7 +35,6 @@
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.DataUsageFeedback;
-import android.telecom.CallIdentification;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -605,69 +604,6 @@
public static final String BLOCK_REASON = "block_reason";
/**
- * The package name of the {@link android.telecom.CallScreeningService} which provided
- * {@link android.telecom.CallIdentification} for this call.
- * <P>Type: TEXT</P>
- */
- public static final String CALL_ID_PACKAGE_NAME = "call_id_package_name";
-
- /**
- * The app name of the {@link android.telecom.CallScreeningService} which provided
- * {@link android.telecom.CallIdentification} for this call.
- * <P>Type: TEXT</P>
- */
- public static final String CALL_ID_APP_NAME = "call_id_app_name";
-
- /**
- * The {@link CallIdentification#getName() name} of a call, as provided by the
- * {@link android.telecom.CallScreeningService}.
- * <p>
- * The name is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
- * {@link #CALL_ID_APP_NAME}.
- * <P>Type: TEXT</P>
- */
- public static final String CALL_ID_NAME = "call_id_name";
-
- /**
- * The {@link CallIdentification#getDescription() description} of a call, as provided by the
- * {@link android.telecom.CallScreeningService}.
- * <p>
- * The description is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
- * {@link #CALL_ID_APP_NAME}.
- * <P>Type: TEXT</P>
- */
- public static final String CALL_ID_DESCRIPTION = "call_id_description";
-
- /**
- * The {@link CallIdentification#getDetails() details} of a call, as provided by the
- * {@link android.telecom.CallScreeningService}.
- * <p>
- * The details field is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
- * {@link #CALL_ID_APP_NAME}.
- * <P>Type: TEXT</P>
- */
- public static final String CALL_ID_DETAILS = "call_id_details";
-
- /**
- * The {@link CallIdentification#getNuisanceConfidence() nuisance confidence} of a call, as
- * provided by the {@link android.telecom.CallScreeningService}.
- * <p>
- * Valid values are defined in {@link CallIdentification}, and include:
- * <ul>
- * <li>{@link CallIdentification#CONFIDENCE_NOT_NUISANCE}</li>
- * <li>{@link CallIdentification#CONFIDENCE_LIKELY_NOT_NUISANCE}</li>
- * <li>{@link CallIdentification#CONFIDENCE_UNKNOWN}</li>
- * <li>{@link CallIdentification#CONFIDENCE_LIKELY_NUISANCE}</li>
- * <li>{@link CallIdentification#CONFIDENCE_NUISANCE}</li>
- * </ul>
- * <p>
- * The nuisance confidence is provided by the app identified by
- * {@link #CALL_ID_PACKAGE_NAME} and {@link #CALL_ID_APP_NAME}.
- * <P>Type: INTEGER</P>
- */
- public static final String CALL_ID_NUISANCE_CONFIDENCE = "call_id_nuisance_confidence";
-
- /**
* Adds a call to the call log.
*
* @param ci the CallerInfo object to get the target contact from. Can be null
@@ -696,8 +632,7 @@
presentation, callType, features, accountHandle, start, duration,
dataUsage, false /* addForAllUsers */, null /* userToBeInsertedTo */,
false /* isRead */, Calls.BLOCK_REASON_NOT_BLOCKED /* callBlockReason */,
- null /* callScreeningAppName */, null /* callScreeningComponentName */,
- null /* callIdentification */);
+ null /* callScreeningAppName */, null /* callScreeningComponentName */);
}
@@ -737,8 +672,7 @@
features, accountHandle, start, duration, dataUsage, addForAllUsers,
userToBeInsertedTo, false /* isRead */ , Calls.BLOCK_REASON_NOT_BLOCKED
/* callBlockReason */, null /* callScreeningAppName */,
- null /* callScreeningComponentName */,
- null /* callIdentification */);
+ null /* callScreeningComponentName */);
}
/**
@@ -784,7 +718,7 @@
int features, PhoneAccountHandle accountHandle, long start, int duration,
Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
boolean isRead, int callBlockReason, CharSequence callScreeningAppName,
- String callScreeningComponentName, CallIdentification callIdentification) {
+ String callScreeningComponentName) {
if (VERBOSE_LOG) {
Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
number, userToBeInsertedTo, addForAllUsers));
@@ -839,26 +773,6 @@
values.put(CALL_SCREENING_APP_NAME, charSequenceToString(callScreeningAppName));
values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
- if (callIdentification != null) {
- values.put(CALL_ID_PACKAGE_NAME, callIdentification.getCallScreeningPackageName());
- values.put(CALL_ID_APP_NAME,
- charSequenceToString(callIdentification.getCallScreeningAppName()));
- values.put(CALL_ID_NAME,
- charSequenceToString(callIdentification.getName()));
- values.put(CALL_ID_DESCRIPTION,
- charSequenceToString(callIdentification.getDescription()));
- values.put(CALL_ID_DETAILS,
- charSequenceToString(callIdentification.getDetails()));
- values.put(CALL_ID_NUISANCE_CONFIDENCE, callIdentification.getNuisanceConfidence());
- } else {
- values.putNull(CALL_ID_PACKAGE_NAME);
- values.putNull(CALL_ID_APP_NAME);
- values.putNull(CALL_ID_NAME);
- values.putNull(CALL_ID_DESCRIPTION);
- values.putNull(CALL_ID_DETAILS);
- values.putNull(CALL_ID_NUISANCE_CONFIDENCE);
- }
-
if ((ci != null) && (ci.contactIdOrZero > 0)) {
// Update usage information for the number associated with the contact ID.
// We need to use both the number and the ID for obtaining a data ID since other
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 227c9d4..85feac8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8654,9 +8654,10 @@
"location_permissions_upgrade_to_q_mode";
/**
- * Comma separated list of enabled overlay packages for all android.theme.customization.*
- * categories. If there is no corresponding package included for a category, then all
- * overlay packages in that category must be disabled.
+ * Map of android.theme.customization.* categories to the enabled overlay package for that
+ * category, formatted as a serialized {@link org.json.JSONObject}. If there is no
+ * corresponding package included for a category, then all overlay packages in that
+ * category must be disabled.
* @hide
*/
@SystemApi
@@ -8664,7 +8665,7 @@
"theme_customization_overlay_packages";
private static final Validator THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR =
- new SettingsValidators.PackageNameListValidator(",");
+ SettingsValidators.JSON_OBJECT_VALIDATOR;
/**
* Controls whether aware is enabled.
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java
index 25e77867..4051213 100644
--- a/core/java/android/provider/SettingsValidators.java
+++ b/core/java/android/provider/SettingsValidators.java
@@ -19,9 +19,13 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.net.Uri;
+import android.text.TextUtils;
import com.android.internal.util.ArrayUtils;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.util.Locale;
/**
@@ -162,6 +166,19 @@
}
};
+ /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
+ public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
+ if (TextUtils.isEmpty(value)) {
+ return false;
+ }
+ try {
+ new JSONObject(value);
+ return true;
+ } catch (JSONException e) {
+ return false;
+ }
+ };
+
public interface Validator {
/**
* Returns whether the input value is valid. Subclasses should handle the case where the
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 7f6423a..5d33b49 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,6 +41,7 @@
public static final String GLOBAL_ACTIONS_GRID_ENABLED = "settings_global_actions_grid_enabled";
public static final String GLOBAL_ACTIONS_PANEL_ENABLED =
"settings_global_actions_panel_enabled";
+ public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -52,7 +53,7 @@
DEFAULT_FLAGS.put("settings_slice_injection", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_mainline_module", "true");
- DEFAULT_FLAGS.put("settings_dynamic_system", "false");
+ DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SAFETY_HUB, "true");
diff --git a/telecomm/java/android/telecom/CallIdentification.aidl b/core/java/android/view/IInputMonitorHost.aidl
similarity index 75%
rename from telecomm/java/android/telecom/CallIdentification.aidl
rename to core/java/android/view/IInputMonitorHost.aidl
index 532535c..bde737d 100644
--- a/telecomm/java/android/telecom/CallIdentification.aidl
+++ b/core/java/android/view/IInputMonitorHost.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright 2018, The Android Open Source Project
+/**
+ * Copyright (c) 2019, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package android.telecom;
+package android.view;
/**
- * {@hide}
+ * @hide
*/
-parcelable CallIdentification;
+oneway interface IInputMonitorHost {
+ void pilferPointers();
+ void dispose();
+}
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index af2b992..ecb727c 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -17,8 +17,8 @@
package android.view;
import android.annotation.UnsupportedAppUsage;
-import android.os.Parcel;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.Parcelable;
import android.util.Slog;
@@ -31,9 +31,9 @@
*/
public final class InputChannel implements Parcelable {
private static final String TAG = "InputChannel";
-
+
private static final boolean DEBUG = false;
-
+
@UnsupportedAppUsage
public static final @android.annotation.NonNull Parcelable.Creator<InputChannel> CREATOR
= new Parcelable.Creator<InputChannel>() {
@@ -42,12 +42,12 @@
result.readFromParcel(source);
return result;
}
-
+
public InputChannel[] newArray(int size) {
return new InputChannel[size];
}
};
-
+
@SuppressWarnings("unused")
@UnsupportedAppUsage
private long mPtr; // used by native code
@@ -81,7 +81,7 @@
super.finalize();
}
}
-
+
/**
* Creates a new input channel pair. One channel should be provided to the input
* dispatcher and the other to the application's input queue.
@@ -100,7 +100,7 @@
}
return nativeOpenInputChannelPair(name);
}
-
+
/**
* Gets the name of the input channel.
* @return The input channel name.
@@ -118,7 +118,7 @@
public void dispose() {
nativeDispose(false);
}
-
+
/**
* Transfers ownership of the internal state of the input channel to another
* instance and invalidates this instance. This is used to pass an input channel
@@ -129,7 +129,7 @@
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
-
+
nativeTransferTo(outParameter);
}
@@ -151,7 +151,7 @@
if (in == null) {
throw new IllegalArgumentException("in must not be null");
}
-
+
nativeReadFromParcel(in);
}
@@ -160,7 +160,7 @@
if (out == null) {
throw new IllegalArgumentException("out must not be null");
}
-
+
nativeWriteToParcel(out);
if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0) {
diff --git a/telecomm/java/android/telecom/CallIdentification.aidl b/core/java/android/view/InputMonitor.aidl
similarity index 73%
copy from telecomm/java/android/telecom/CallIdentification.aidl
copy to core/java/android/view/InputMonitor.aidl
index 532535c..bdd14fe 100644
--- a/telecomm/java/android/telecom/CallIdentification.aidl
+++ b/core/java/android/view/InputMonitor.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2018, The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-package android.telecom;
+package android.view;
-/**
- * {@hide}
- */
-parcelable CallIdentification;
+parcelable InputMonitor;
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
new file mode 100644
index 0000000..693f287
--- /dev/null
+++ b/core/java/android/view/InputMonitor.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * @hide
+ */
+public final class InputMonitor implements Parcelable {
+ private static final String TAG = "InputMonitor";
+
+ private static final boolean DEBUG = false;
+
+ public static final Parcelable.Creator<InputMonitor> CREATOR =
+ new Parcelable.Creator<InputMonitor>() {
+
+ public InputMonitor createFromParcel(Parcel source) {
+ return new InputMonitor(source);
+ }
+
+ public InputMonitor[] newArray(int size) {
+ return new InputMonitor[size];
+ }
+ };
+
+ @NonNull
+ private final String mName;
+ @NonNull
+ private final InputChannel mChannel;
+ @NonNull
+ private final IInputMonitorHost mHost;
+
+ public InputMonitor(@NonNull String name, @NonNull InputChannel channel,
+ @NonNull IInputMonitorHost host) {
+ mName = name;
+ mChannel = channel;
+ mHost = host;
+ }
+
+ public InputMonitor(Parcel in) {
+ mName = in.readString();
+ mChannel = in.readParcelable(null);
+ mHost = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
+ }
+
+ /**
+ * Get the {@link InputChannel} corresponding to this InputMonitor
+ */
+ public InputChannel getInputChannel() {
+ return mChannel;
+ }
+
+ /**
+ * Get the name of this channel.
+ */
+ public String getName() {
+ return mName;
+ }
+
+
+ /**
+ * Takes all of the current pointer events streams that are currently being sent to this
+ * monitor and generates appropriate cancellations for the windows that would normally get
+ * them.
+ *
+ * This method should be used with caution as unexpected pilfering can break fundamental user
+ * interactions.
+ */
+ public void pilferPointers() {
+ try {
+ mHost.pilferPointers();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disposes the input monitor.
+ *
+ * Explicitly release all of the resources this monitor is holding on to (e.g. the
+ * InputChannel). Once this method is called, this monitor and any resources it's provided may
+ * no longer be used.
+ */
+ public void dispose() {
+ mChannel.dispose();
+ try {
+ mHost.dispose();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mName);
+ out.writeParcelable(mChannel, flags);
+ out.writeStrongBinder(mHost.asBinder());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "InputMonitor{mName=" + mName + ", mChannel=" + mChannel + ", mHost=" + mHost + "}";
+ }
+}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 35cf129..f9b629c8 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -566,7 +566,7 @@
String layout = res.getResourceEntryName(resource);
try {
- Class clazz = mPrecompiledClassLoader.loadClass("" + pkg + ".CompiledView");
+ Class clazz = Class.forName("" + pkg + ".CompiledView", false, mPrecompiledClassLoader);
Method inflater = clazz.getMethod(layout, Context.class, int.class);
View view = (View) inflater.invoke(null, mContext, resource);
@@ -827,8 +827,8 @@
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
- clazz = mContext.getClassLoader().loadClass(
- prefix != null ? (prefix + name) : name).asSubclass(View.class);
+ clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
+ mContext.getClassLoader()).asSubclass(View.class);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
@@ -846,8 +846,8 @@
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowed
- clazz = mContext.getClassLoader().loadClass(
- prefix != null ? (prefix + name) : name).asSubclass(View.class);
+ clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
+ mContext.getClassLoader()).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
diff --git a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
index b034846..22e374f2 100644
--- a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
+++ b/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
@@ -48,6 +48,7 @@
context.getString(com.android.internal.R.string.translate),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.translate_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_TRANSLATE)
// TODO: Probably better to introduce a "translate" scheme instead of
// using EXTRA_TEXT.
diff --git a/core/java/android/view/textclassifier/intent/LabeledIntent.java b/core/java/android/view/textclassifier/intent/LabeledIntent.java
index 11d64f1..b4bc8d3 100644
--- a/core/java/android/view/textclassifier/intent/LabeledIntent.java
+++ b/core/java/android/view/textclassifier/intent/LabeledIntent.java
@@ -56,6 +56,8 @@
@Nullable
public final String titleWithEntity;
public final String description;
+ @Nullable
+ public final String descriptionWithAppName;
// Do not update this intent.
public final Intent intent;
public final int requestCode;
@@ -75,6 +77,7 @@
@Nullable String titleWithoutEntity,
@Nullable String titleWithEntity,
String description,
+ @Nullable String descriptionWithAppName,
Intent intent,
int requestCode) {
if (TextUtils.isEmpty(titleWithEntity) && TextUtils.isEmpty(titleWithoutEntity)) {
@@ -84,6 +87,7 @@
this.titleWithoutEntity = titleWithoutEntity;
this.titleWithEntity = titleWithEntity;
this.description = Preconditions.checkNotNull(description);
+ this.descriptionWithAppName = descriptionWithAppName;
this.intent = Preconditions.checkNotNull(intent);
this.requestCode = requestCode;
}
@@ -141,11 +145,39 @@
Log.w(TAG, "Custom titleChooser return null, fallback to the default titleChooser");
title = DEFAULT_TITLE_CHOOSER.chooseTitle(this, resolveInfo);
}
- final RemoteAction action = new RemoteAction(icon, title, description, pendingIntent);
+ final RemoteAction action =
+ new RemoteAction(icon, title, resolveDescription(resolveInfo, pm), pendingIntent);
action.setShouldShowIcon(shouldShowIcon);
return new Result(resolvedIntent, action);
}
+ private String resolveDescription(ResolveInfo resolveInfo, PackageManager packageManager) {
+ if (!TextUtils.isEmpty(descriptionWithAppName)) {
+ // Example string format of descriptionWithAppName: "Use %1$s to open map".
+ String applicationName = getApplicationName(resolveInfo, packageManager);
+ if (!TextUtils.isEmpty(applicationName)) {
+ return String.format(descriptionWithAppName, applicationName);
+ }
+ }
+ return description;
+ }
+
+ @Nullable
+ private String getApplicationName(
+ ResolveInfo resolveInfo, PackageManager packageManager) {
+ if (resolveInfo.activityInfo == null) {
+ return null;
+ }
+ if ("android".equals(resolveInfo.activityInfo.packageName)) {
+ return null;
+ }
+ if (resolveInfo.activityInfo.applicationInfo == null) {
+ return null;
+ }
+ return (String) packageManager.getApplicationLabel(
+ resolveInfo.activityInfo.applicationInfo);
+ }
+
private Bundle getFromTextClassifierExtra(@Nullable Bundle textLanguagesBundle) {
if (textLanguagesBundle != null) {
final Bundle bundle = new Bundle();
diff --git a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
index 7916791..8d60ad8 100644
--- a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
+++ b/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
@@ -46,6 +46,7 @@
* Creates intents based on the classification type.
* @hide
*/
+// TODO: Consider to support {@code descriptionWithAppName}.
public final class LegacyClassificationIntentFactory implements ClassificationIntentFactory {
private static final String TAG = "LegacyClassificationIntentFactory";
@@ -108,6 +109,7 @@
context.getString(com.android.internal.R.string.email),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.email_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_SENDTO)
.setData(Uri.parse(String.format("mailto:%s", text))),
LabeledIntent.DEFAULT_REQUEST_CODE));
@@ -115,6 +117,7 @@
context.getString(com.android.internal.R.string.add_contact),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.add_contact_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_INSERT_OR_EDIT)
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
.putExtra(ContactsContract.Intents.Insert.EMAIL, text),
@@ -133,6 +136,7 @@
context.getString(com.android.internal.R.string.dial),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.dial_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_DIAL).setData(
Uri.parse(String.format("tel:%s", text))),
LabeledIntent.DEFAULT_REQUEST_CODE));
@@ -141,6 +145,7 @@
context.getString(com.android.internal.R.string.add_contact),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.add_contact_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_INSERT_OR_EDIT)
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
.putExtra(ContactsContract.Intents.Insert.PHONE, text),
@@ -150,6 +155,7 @@
context.getString(com.android.internal.R.string.sms),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.sms_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_SENDTO)
.setData(Uri.parse(String.format("smsto:%s", text))),
LabeledIntent.DEFAULT_REQUEST_CODE));
@@ -166,6 +172,7 @@
context.getString(com.android.internal.R.string.map),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.map_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
LabeledIntent.DEFAULT_REQUEST_CODE));
@@ -185,6 +192,7 @@
context.getString(com.android.internal.R.string.browse),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.browse_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_VIEW)
.setDataAndNormalize(Uri.parse(text))
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
@@ -216,6 +224,7 @@
context.getString(com.android.internal.R.string.view_flight),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.view_flight_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_WEB_SEARCH)
.putExtra(SearchManager.QUERY, text),
text.hashCode()));
@@ -231,6 +240,7 @@
context.getString(com.android.internal.R.string.view_calendar),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.view_calendar_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_VIEW).setData(builder.build()),
LabeledIntent.DEFAULT_REQUEST_CODE);
}
@@ -243,6 +253,7 @@
context.getString(com.android.internal.R.string.add_calendar_event),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.add_calendar_event_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_INSERT)
.setData(CalendarContract.Events.CONTENT_URI)
.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
@@ -260,6 +271,7 @@
context.getString(com.android.internal.R.string.define),
/* titleWithEntity */ null,
context.getString(com.android.internal.R.string.define_desc),
+ /* descriptionWithAppName */ null,
new Intent(Intent.ACTION_DEFINE)
.putExtra(Intent.EXTRA_TEXT, text),
text.hashCode()));
diff --git a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java b/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
index 59cd7ab..7a39569 100644
--- a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
+++ b/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
@@ -61,6 +61,7 @@
remoteActionTemplate.titleWithoutEntity,
remoteActionTemplate.titleWithEntity,
remoteActionTemplate.description,
+ remoteActionTemplate.descriptionWithAppName,
createIntent(remoteActionTemplate),
remoteActionTemplate.requestCode == null
? LabeledIntent.DEFAULT_REQUEST_CODE
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 1c90182..72dbbf3 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -26,8 +26,9 @@
import com.android.internal.app.IAppOpsNotedCallback;
interface IAppOpsService {
- // These first methods are also called by native code, so must
+ // These methods are also called by native code, so must
// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
+ // and not be reordered
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName);
int startOperation(IBinder token, int code, int uid, String packageName,
@@ -38,6 +39,10 @@
void stopWatchingMode(IAppOpsCallback callback);
IBinder getToken(IBinder clientToken);
int permissionToOpCode(String permission);
+ int checkAudioOperation(int code, int usage, int uid, String packageName);
+ // End of methods also called by native code.
+ // Any new method exposed to native must be added after the last one, do not reorder
+
int noteProxyOperation(int code, int proxyUid, String proxyPackageName,
int callingUid, String callingPackageName);
@@ -62,7 +67,6 @@
void setMode(int code, int uid, String packageName, int mode);
@UnsupportedAppUsage
void resetAllModes(int reqUserId, String reqPackageName);
- int checkAudioOperation(int code, int usage, int uid, String packageName);
void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages);
void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle);
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 0e4897f..b26efc0 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -79,6 +79,7 @@
STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_TOP
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3686048..d92f725b 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -21,6 +21,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import java.io.IOException;
@@ -29,6 +30,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.function.Predicate;
/**
@@ -233,7 +235,7 @@
mFrequencyBucketCreator =
new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
mFrequenciesKhz =
- mFrequencyBucketCreator.getBucketMinFrequencies(
+ mFrequencyBucketCreator.bucketFrequencies(
mProcTimeInStateReader.getFrequenciesKhz());
}
@@ -317,7 +319,7 @@
if (cpuUsagesLong == null) {
return null;
}
- int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
+ int[] cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong);
return new ThreadCpuUsage(threadId, threadName, cpuUsages);
}
@@ -359,89 +361,46 @@
}
}
- /** Puts frequencies and usage times into buckets */
+ /**
+ * Quantizes a list of N frequencies into a list of M frequencies (where M<=N)
+ *
+ * <p>In order to reduce data sent from the device, we discard precise frequency information for
+ * an approximation. This is done by putting groups of adjacent frequencies into the same
+ * bucket, and then reporting that bucket under the minimum frequency in that bucket.
+ *
+ * <p>Many devices have multiple core clusters. We do not want to report frequencies from
+ * different clusters under the same bucket, so some complication arises.
+ *
+ * <p>Buckets are allocated evenly across all core clusters, i.e. they all have the same number
+ * of buckets regardless of how many frequencies they contain. This is done to reduce code
+ * complexity, and in practice the number of frequencies doesn't vary too much between core
+ * clusters.
+ *
+ * <p>If the number of buckets is not a factor of the number of frequencies, the remainder of
+ * the frequencies are placed into the last bucket.
+ *
+ * <p>It is possible to have less buckets than asked for, so any calling code can't assume that
+ * initializing with N buckets will use return N values. This happens in two scenarios:
+ *
+ * <ul>
+ * <li>There are less frequencies available than buckets asked for.
+ * <li>There are less frequencies in a core cluster than buckets allocated to that core
+ * cluster.
+ * </ul>
+ */
@VisibleForTesting
public static class FrequencyBucketCreator {
- private final int mNumBuckets;
private final int mNumFrequencies;
- private final int mBigFrequenciesStartIndex;
- private final int mLittleNumBuckets;
- private final int mBigNumBuckets;
- private final int mLittleBucketSize;
- private final int mBigBucketSize;
+ private final int mNumBuckets;
+ private final int[] mBucketStartIndices;
- /**
- * Buckets based of a list of frequencies
- *
- * @param frequencies the frequencies to base buckets off
- * @param numBuckets how many buckets to create
- */
@VisibleForTesting
- public FrequencyBucketCreator(long[] frequencies, int numBuckets) {
- Preconditions.checkArgument(numBuckets > 0);
-
+ public FrequencyBucketCreator(long[] frequencies, int targetNumBuckets) {
mNumFrequencies = frequencies.length;
- mBigFrequenciesStartIndex = getBigFrequenciesStartIndex(frequencies);
-
- final int littleNumBuckets;
- final int bigNumBuckets;
- if (mBigFrequenciesStartIndex < frequencies.length) {
- littleNumBuckets = numBuckets / 2;
- bigNumBuckets = numBuckets - littleNumBuckets;
- } else {
- // If we've got no big frequencies, set all buckets to little frequencies
- littleNumBuckets = numBuckets;
- bigNumBuckets = 0;
- }
-
- // Ensure that we don't have more buckets than frequencies
- mLittleNumBuckets = Math.min(littleNumBuckets, mBigFrequenciesStartIndex);
- mBigNumBuckets =
- Math.min(bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex);
- mNumBuckets = mLittleNumBuckets + mBigNumBuckets;
-
- // Set the size of each little and big bucket. If they have no buckets, the size is zero
- mLittleBucketSize =
- mLittleNumBuckets == 0 ? 0 : mBigFrequenciesStartIndex / mLittleNumBuckets;
- mBigBucketSize =
- mBigNumBuckets == 0
- ? 0
- : (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets;
- }
-
- /** Find the index where frequencies change from little core to big core */
- @VisibleForTesting
- public static int getBigFrequenciesStartIndex(long[] frequenciesKhz) {
- for (int i = 0; i < frequenciesKhz.length - 1; i++) {
- if (frequenciesKhz[i] > frequenciesKhz[i + 1]) {
- return i + 1;
- }
- }
-
- return frequenciesKhz.length;
- }
-
- /** Get the minimum frequency in each bucket */
- @VisibleForTesting
- public int[] getBucketMinFrequencies(long[] frequenciesKhz) {
- Preconditions.checkArgument(frequenciesKhz.length == mNumFrequencies);
- // If there's only one bucket, we bucket everything together so the first bucket is the
- // min frequency
- if (mNumBuckets == 1) {
- return new int[] {(int) frequenciesKhz[0]};
- }
-
- final int[] bucketMinFrequencies = new int[mNumBuckets];
- // Initialize little buckets min frequencies
- for (int i = 0; i < mLittleNumBuckets; i++) {
- bucketMinFrequencies[i] = (int) frequenciesKhz[i * mLittleBucketSize];
- }
- // Initialize big buckets min frequencies
- for (int i = 0; i < mBigNumBuckets; i++) {
- final int frequencyIndex = mBigFrequenciesStartIndex + i * mBigBucketSize;
- bucketMinFrequencies[mLittleNumBuckets + i] = (int) frequenciesKhz[frequencyIndex];
- }
- return bucketMinFrequencies;
+ int[] clusterStartIndices = getClusterStartIndices(frequencies);
+ mBucketStartIndices =
+ getBucketStartIndices(clusterStartIndices, targetNumBuckets, mNumFrequencies);
+ mNumBuckets = mBucketStartIndices.length;
}
/**
@@ -453,34 +412,105 @@
* @return the bucketed usage times
*/
@VisibleForTesting
- @SuppressWarnings("ForLoopReplaceableByForEach")
- public int[] getBucketedValues(long[] values) {
+ public int[] bucketValues(long[] values) {
Preconditions.checkArgument(values.length == mNumFrequencies);
- final int[] bucketed = new int[mNumBuckets];
-
- // If there's only one bucket, add all frequencies in
- if (mNumBuckets == 1) {
- for (int i = 0; i < values.length; i++) {
- bucketed[0] += values[i];
+ int[] buckets = new int[mNumBuckets];
+ for (int bucketIdx = 0; bucketIdx < mNumBuckets; bucketIdx++) {
+ final int bucketStartIdx = getLowerBound(bucketIdx, mBucketStartIndices);
+ final int bucketEndIdx =
+ getUpperBound(bucketIdx, mBucketStartIndices, values.length);
+ for (int valuesIdx = bucketStartIdx; valuesIdx < bucketEndIdx; valuesIdx++) {
+ buckets[bucketIdx] += values[valuesIdx];
}
- return bucketed;
+ }
+ return buckets;
+ }
+
+ /** Get the minimum frequency in each bucket */
+ @VisibleForTesting
+ public int[] bucketFrequencies(long[] frequencies) {
+ Preconditions.checkArgument(frequencies.length == mNumFrequencies);
+ int[] buckets = new int[mNumBuckets];
+ for (int i = 0; i < buckets.length; i++) {
+ buckets[i] = (int) frequencies[mBucketStartIndices[i]];
+ }
+ return buckets;
+ }
+
+ /**
+ * Get the index in frequencies where each core cluster starts
+ *
+ * <p>The frequencies for each cluster are given in ascending order, appended to each other.
+ * This means that every time there is a decrease in frequencies (instead of increase) a new
+ * cluster has started.
+ */
+ private static int[] getClusterStartIndices(long[] frequencies) {
+ ArrayList<Integer> indices = new ArrayList<>();
+ indices.add(0);
+ for (int i = 0; i < frequencies.length - 1; i++) {
+ if (frequencies[i] >= frequencies[i + 1]) {
+ indices.add(i + 1);
+ }
+ }
+ return ArrayUtils.convertToIntArray(indices);
+ }
+
+ /** Get the index in frequencies where each bucket starts */
+ private static int[] getBucketStartIndices(
+ int[] clusterStartIndices, int targetNumBuckets, int numFrequencies) {
+ int numClusters = clusterStartIndices.length;
+
+ // If we haven't got enough buckets for every cluster, we instead have one bucket per
+ // cluster, with the last bucket containing the remaining clusters
+ if (numClusters > targetNumBuckets) {
+ return Arrays.copyOfRange(clusterStartIndices, 0, targetNumBuckets);
}
- // Initialize the little buckets
- for (int i = 0; i < mBigFrequenciesStartIndex; i++) {
- final int bucketIndex = Math.min(i / mLittleBucketSize, mLittleNumBuckets - 1);
- bucketed[bucketIndex] += values[i];
+ ArrayList<Integer> bucketStartIndices = new ArrayList<>();
+ for (int clusterIdx = 0; clusterIdx < numClusters; clusterIdx++) {
+ final int clusterStartIdx = getLowerBound(clusterIdx, clusterStartIndices);
+ final int clusterEndIdx =
+ getUpperBound(clusterIdx, clusterStartIndices, numFrequencies);
+
+ final int numBucketsInCluster;
+ if (clusterIdx != numClusters - 1) {
+ numBucketsInCluster = targetNumBuckets / numClusters;
+ } else {
+ // If we're in the last cluster, the bucket will contain the remainder of the
+ // frequencies
+ int previousBucketsInCluster = targetNumBuckets / numClusters;
+ numBucketsInCluster =
+ targetNumBuckets - (previousBucketsInCluster * (numClusters - 1));
+ }
+
+ final int numFrequenciesInCluster = clusterEndIdx - clusterStartIdx;
+ // If there are less frequencies than buckets in a cluster, we have one bucket per
+ // frequency, and do not use the remaining buckets
+ final int numFrequenciesInBucket =
+ Math.max(1, numFrequenciesInCluster / numBucketsInCluster);
+ for (int bucketIdx = 0; bucketIdx < numBucketsInCluster; bucketIdx++) {
+ int bucketStartIdx = clusterStartIdx + bucketIdx * numFrequenciesInBucket;
+ // If we've gone over the end index, ignore the rest of the buckets for this
+ // cluster
+ if (bucketStartIdx >= clusterEndIdx) {
+ break;
+ }
+ bucketStartIndices.add(bucketStartIdx);
+ }
}
- // Initialize the big buckets
- for (int i = mBigFrequenciesStartIndex; i < values.length; i++) {
- final int bucketIndex =
- Math.min(
- mLittleNumBuckets
- + (i - mBigFrequenciesStartIndex) / mBigBucketSize,
- mNumBuckets - 1);
- bucketed[bucketIndex] += values[i];
+ return ArrayUtils.convertToIntArray(bucketStartIndices);
+ }
+
+ private static int getLowerBound(int index, int[] startIndices) {
+ return startIndices[index];
+ }
+
+ private static int getUpperBound(int index, int[] startIndices, int max) {
+ if (index != startIndices.length - 1) {
+ return startIndices[index + 1];
+ } else {
+ return max;
}
- return bucketed;
}
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index ca5db94..625814d 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1143,7 +1143,6 @@
// If we didn't request fullscreen layout, but we still got it because of the
// mForceWindowDrawsStatusBarBackground flag, also consume top inset.
boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
- && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
&& (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
&& mForceWindowDrawsStatusBarBackground
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 86cda44..5e4d6e3 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -92,7 +92,9 @@
CursorWindow* window;
status_t status = CursorWindow::create(name, cursorWindowSize, &window);
if (status || !window) {
- ALOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.",
+ jniThrowExceptionFmt(env,
+ "android/database/CursorWindowAllocationException",
+ "Could not allocate CursorWindow '%s' of size %d due to error %d.",
name.string(), cursorWindowSize, status);
return 0;
}
@@ -107,7 +109,9 @@
CursorWindow* window;
status_t status = CursorWindow::createFromParcel(parcel, &window);
if (status || !window) {
- ALOGE("Could not create CursorWindow from Parcel due to error %d, process fd count=%d",
+ jniThrowExceptionFmt(env,
+ "android/database/CursorWindowAllocationException",
+ "Could not create CursorWindow from Parcel due to error %d, process fd count=%d",
status, getFdCount());
return 0;
}
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index c7805ea..03057dc 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -21,6 +21,7 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
#include "core_jni_helpers.h"
#include <system/sound_trigger.h>
#include <soundtrigger/SoundTriggerCallback.h>
@@ -395,7 +396,7 @@
static jint
android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz,
- jobject jModules)
+ jstring opPackageName, jobject jModules)
{
ALOGV("listModules");
@@ -411,7 +412,10 @@
unsigned int numModules = 0;
struct sound_trigger_module_descriptor *nModules = NULL;
- status_t status = SoundTrigger::listModules(nModules, &numModules);
+ ScopedUtfChars opPackageNameStr(env, opPackageName);
+ const String16 opPackageNameString16 = String16(opPackageNameStr.c_str());
+
+ status_t status = SoundTrigger::listModules(opPackageNameString16, nModules, &numModules);
if (status != NO_ERROR || numModules == 0) {
return (jint)status;
}
@@ -419,7 +423,7 @@
nModules = (struct sound_trigger_module_descriptor *)
calloc(numModules, sizeof(struct sound_trigger_module_descriptor));
- status = SoundTrigger::listModules(nModules, &numModules);
+ status = SoundTrigger::listModules(opPackageNameString16, nModules, &numModules);
ALOGV("listModules SoundTrigger::listModules status %d numModules %d", status, numModules);
if (status != NO_ERROR) {
@@ -470,16 +474,20 @@
}
static void
-android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz, jobject weak_this)
+android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz,
+ jstring opPackageName, jobject weak_this)
{
ALOGV("setup");
+ ScopedUtfChars opPackageNameStr(env, opPackageName);
+ const String16 opPackageNameString16 = String16(opPackageNameStr.c_str());
+
sp<JNISoundTriggerCallback> callback = new JNISoundTriggerCallback(env, thiz, weak_this);
sound_trigger_module_handle_t handle =
(sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId);
- sp<SoundTrigger> module = SoundTrigger::attach(handle, callback);
+ sp<SoundTrigger> module = SoundTrigger::attach(opPackageNameString16, handle, callback);
if (module == 0) {
return;
}
@@ -816,14 +824,14 @@
static const JNINativeMethod gMethods[] = {
{"listModules",
- "(Ljava/util/ArrayList;)I",
+ "(Ljava/lang/String;Ljava/util/ArrayList;)I",
(void *)android_hardware_SoundTrigger_listModules},
};
static const JNINativeMethod gModuleMethods[] = {
{"native_setup",
- "(Ljava/lang/Object;)V",
+ "(Ljava/lang/String;Ljava/lang/Object;)V",
(void *)android_hardware_SoundTrigger_setup},
{"native_finalize",
"()V",
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 8cb1078..236ee61 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -24,10 +24,30 @@
#include "selinux/android.h"
#include <errno.h>
#include <memory>
+#include <atomic>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedUtfChars.h>
namespace android {
+namespace {
+std::atomic<selabel_handle*> sehandle{nullptr};
+
+selabel_handle* GetSELabelHandle() {
+ selabel_handle* h = sehandle.load();
+ if (h != nullptr) {
+ return h;
+ }
+
+ h = selinux_android_file_context_handle();
+ selabel_handle* expected = nullptr;
+ if (!sehandle.compare_exchange_strong(expected, h)) {
+ selabel_close(h);
+ return sehandle.load();
+ }
+ return h;
+}
+
+}
struct SecurityContext_Delete {
void operator()(security_context_t p) const {
@@ -60,6 +80,44 @@
return (security_getenforce() == 1) ? true : false;
}
+static jstring fileSelabelLookup(JNIEnv* env, jobject, jstring pathStr) {
+ if (isSELinuxDisabled) {
+ ALOGE("fileSelabelLookup => SELinux is disabled");
+ return NULL;
+ }
+
+ if (pathStr == NULL) {
+ ALOGE("fileSelabelLookup => got null path.");
+ jniThrowNullPointerException(
+ env, "Trying to get security context of a null path.");
+ return NULL;
+ }
+
+ ScopedUtfChars path(env, pathStr);
+ const char* path_c_str = path.c_str();
+ if (path_c_str == NULL) {
+ ALOGE("fileSelabelLookup => Got null path");
+ jniThrowNullPointerException(
+ env, "Trying to get security context of a null path.");
+ return NULL;
+ }
+
+ auto* selabel_handle = GetSELabelHandle();
+ if (selabel_handle == NULL) {
+ ALOGE("fileSelabelLookup => Failed to get SEHandle");
+ return NULL;
+ }
+
+ security_context_t tmp = NULL;
+ if (selabel_lookup(selabel_handle, &tmp, path_c_str, S_IFREG) != 0) {
+ ALOGE("fileSelabelLookup => selabel_lookup for %s failed: %d", path_c_str, errno);
+ return NULL;
+ }
+
+ Unique_SecurityContext context(tmp);
+ return env->NewStringUTF(context.get());
+}
+
static jstring getFdConInner(JNIEnv *env, jobject fileDescriptor, bool isSocket) {
if (isSELinuxDisabled) {
return NULL;
@@ -354,6 +412,7 @@
{ "native_restorecon" , "(Ljava/lang/String;I)Z" , (void*)native_restorecon},
{ "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon },
{ "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon },
+ { "fileSelabelLookup" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)fileSelabelLookup},
};
static int log_callback(int type, const char *fmt, ...) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index bf56ef4..d3f9196 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -163,7 +163,7 @@
}
// Generic idmap parameters
- const char* argv[8];
+ const char* argv[9];
int argc = 0;
struct stat st;
@@ -199,6 +199,10 @@
argv[argc++] = AssetManager::PRODUCT_SERVICES_OVERLAY_DIR;
}
+ if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::ODM_OVERLAY_DIR;
+ }
+
// Finally, invoke idmap (if any overlay directory exists)
if (argc > 5) {
execv(AssetManager::IDMAP_BIN, (char* const*)argv);
@@ -233,6 +237,10 @@
input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR);
}
+ if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
+ input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR);
+ }
+
if (input_dirs.empty()) {
LOG(WARNING) << "no directories for idmap2 to scan";
return env->NewObjectArray(0, g_stringClass, nullptr);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 70b3436..8dd7e8e 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1634,6 +1634,8 @@
// security_getenforce is not allowed on app process. Initialize and cache
// the value before zygote forks.
g_is_security_enforced = security_getenforce();
+
+ selinux_android_seapp_context_init();
}
static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index d8d4656..0996352 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -102,6 +102,8 @@
static const char* kProductOverlayDir = "/product/overlay";
static const char* kSystemProductServicesOverlayDir = "/system/product_services/overlay/";
static const char* kProductServicesOverlayDir = "/product_services/overlay";
+ static const char* kSystemOdmOverlayDir = "/system/odm/overlay";
+ static const char* kOdmOverlayDir = "/odm/overlay";
static const char* kApkSuffix = ".apk";
if ((android::base::StartsWith(path, kOverlayDir)
@@ -110,7 +112,9 @@
|| android::base::StartsWith(path, kSystemProductOverlayDir)
|| android::base::StartsWith(path, kProductOverlayDir)
|| android::base::StartsWith(path, kSystemProductServicesOverlayDir)
- || android::base::StartsWith(path, kProductServicesOverlayDir))
+ || android::base::StartsWith(path, kProductServicesOverlayDir)
+ || android::base::StartsWith(path, kSystemOdmOverlayDir)
+ || android::base::StartsWith(path, kOdmOverlayDir))
&& android::base::EndsWith(path, kApkSuffix)
&& path.find("/../") == std::string::npos) {
return true;
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
index 1754e42..9abe923 100644
--- a/core/proto/android/app/enums.proto
+++ b/core/proto/android/app/enums.proto
@@ -38,6 +38,7 @@
}
// ActivityManager.java PROCESS_STATEs
+// Next tag: 1021
enum ProcessStateEnum {
// Unlike the ActivityManager PROCESS_STATE values, the ordering and numerical values
// here are completely fixed and arbitrary. Order is irrelevant.
@@ -56,9 +57,11 @@
// Process is hosting the current top activities. Note that this covers
// all activities that are visible to the user.
PROCESS_STATE_TOP = 1002;
+ // Process is bound to a TOP app.
+ PROCESS_STATE_BOUND_TOP = 1020;
// Process is hosting a foreground service.
PROCESS_STATE_FOREGROUND_SERVICE = 1003;
- // Process is hosting a foreground service due to a system binding.
+ // Process is hosting a service bound by the system or another foreground app.
PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 1004;
// Process is important to the user, and something they are aware of.
PROCESS_STATE_IMPORTANT_FOREGROUND = 1005;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3788f47..fb92fbf 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4506,6 +4506,10 @@
<permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
android:protectionLevel="signature|installer" />
+ <!-- Allows input events to be monitored. Very dangerous! @hide -->
+ <permission android:name="android.permission.MONITOR_INPUT"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/layout-car/car_preference.xml b/core/res/res/layout-car/car_preference.xml
index 939c3fb..ae3d63b 100644
--- a/core/res/res/layout-car/car_preference.xml
+++ b/core/res/res/layout-car/car_preference.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
- android:focusable="true"
+ android:clipToPadding="false"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingStart="?android:attr/listPreferredItemPaddingStart">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b92033e..4b8a96c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1567,7 +1567,7 @@
<string name="face_error_lockout_permanent">Too many attempts. Face authentication disabled.</string>
<!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
<string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string>
- <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] -->
+ <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=52] -->
<string name="face_error_not_enrolled">You haven\u2019t set up face authentication</string>
<!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] -->
<string name="face_error_hw_not_present">Face authentication is not supported on this device</string>
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
index 08f9de6..8081e9e 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
@@ -16,6 +16,8 @@
package android.provider;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -26,6 +28,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -218,6 +222,31 @@
}
@Test
+ public void testJSONObjectValidator() throws JSONException {
+ Validator v = SettingsValidators.JSON_OBJECT_VALIDATOR;
+
+ assertThat(v.validate(new JSONObject().toString())).isTrue();
+ assertThat(v.validate("{}")).isTrue();
+ assertThat(v.validate(new JSONObject().put("foo", "bar").toString()))
+ .isTrue();
+ assertThat(v.validate("{\"foo\": \"bar\"}")).isTrue();
+
+ assertThat(v.validate("random string")).isFalse();
+ assertThat(v.validate("random: string")).isFalse();
+ assertThat(v.validate("{random: }")).isFalse();
+ }
+
+ @Test
+ public void testJSONObjectValidator_onNullValue_returnsFalse() {
+ assertThat(SettingsValidators.JSON_OBJECT_VALIDATOR.validate(null)).isFalse();
+ }
+
+ @Test
+ public void testJSONObjectValidator_onEmptyString_returnsFalse() {
+ assertThat(SettingsValidators.JSON_OBJECT_VALIDATOR.validate("")).isFalse();
+ }
+
+ @Test
public void ensureAllBackedUpSystemSettingsHaveValidators() {
String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
index 67423c8..db5f82a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -263,6 +263,7 @@
"title",
null,
"description",
+ null,
Intent.ACTION_VIEW,
Uri.parse("http://www.android.com").toString(),
null,
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
index 2674e37..11bea0c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -54,6 +54,7 @@
private final PackageManager mPackageManager;
private final ContextWrapper mContext;
private final Map<String, ComponentName> mComponents = new HashMap<>();
+ private final Map<String, CharSequence> mAppLabels = new HashMap<>();
private @Nullable ComponentName mAllIntentComponent;
public FakeContextBuilder() {
@@ -79,6 +80,14 @@
return this;
}
+ /**
+ * Sets the app label res for a specified package.
+ */
+ public FakeContextBuilder setAppLabel(String packageName, @Nullable CharSequence appLabel) {
+ Preconditions.checkNotNull(packageName);
+ mAppLabels.put(packageName, appLabel);
+ return this;
+ }
/**
* Sets the component name of an activity to handle all intents.
@@ -102,6 +111,11 @@
: mAllIntentComponent;
return getResolveInfo(component);
});
+ when(mPackageManager.getApplicationLabel(any(ApplicationInfo.class))).thenAnswer(
+ (Answer<CharSequence>) invocation -> {
+ ApplicationInfo applicationInfo = invocation.getArgument(0);
+ return mAppLabels.get(applicationInfo.packageName);
+ });
return mContext;
}
@@ -125,6 +139,7 @@
info.activityInfo.name = component.getClassName();
info.activityInfo.exported = true;
info.activityInfo.applicationInfo = new ApplicationInfo();
+ info.activityInfo.applicationInfo.packageName = component.getPackageName();
info.activityInfo.applicationInfo.icon = 0;
}
return info;
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
index 857408f..3ad26f5 100644
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
@@ -20,6 +20,7 @@
import static org.testng.Assert.assertThrows;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -40,17 +41,21 @@
private static final String TITLE_WITHOUT_ENTITY = "Map";
private static final String TITLE_WITH_ENTITY = "Map NW14D1";
private static final String DESCRIPTION = "Check the map";
+ private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
private static final Intent INTENT =
new Intent(Intent.ACTION_VIEW).setDataAndNormalize(Uri.parse("http://www.android.com"));
private static final int REQUEST_CODE = 42;
private static final Bundle TEXT_LANGUAGES_BUNDLE = Bundle.EMPTY;
+ private static final String APP_LABEL = "fake";
private Context mContext;
@Before
public void setup() {
+ final ComponentName component = FakeContextBuilder.DEFAULT_COMPONENT;
mContext = new FakeContextBuilder()
- .setIntentComponent(Intent.ACTION_VIEW, FakeContextBuilder.DEFAULT_COMPONENT)
+ .setIntentComponent(Intent.ACTION_VIEW, component)
+ .setAppLabel(component.getPackageName(), APP_LABEL)
.build();
}
@@ -60,6 +65,7 @@
TITLE_WITHOUT_ENTITY,
TITLE_WITH_ENTITY,
DESCRIPTION,
+ null,
INTENT,
REQUEST_CODE
);
@@ -82,6 +88,7 @@
TITLE_WITHOUT_ENTITY,
null,
DESCRIPTION,
+ null,
INTENT,
REQUEST_CODE
);
@@ -103,6 +110,7 @@
TITLE_WITHOUT_ENTITY,
null,
DESCRIPTION,
+ null,
INTENT,
REQUEST_CODE
);
@@ -124,6 +132,7 @@
TITLE_WITHOUT_ENTITY,
null,
DESCRIPTION,
+ null,
INTENT,
REQUEST_CODE
);
@@ -148,6 +157,7 @@
null,
null,
DESCRIPTION,
+ null,
INTENT,
REQUEST_CODE
));
@@ -161,6 +171,7 @@
TITLE_WITHOUT_ENTITY,
null,
DESCRIPTION,
+ null,
unresolvableIntent,
REQUEST_CODE);
@@ -168,4 +179,22 @@
assertThat(result).isNull();
}
+
+ @Test
+ public void resolve_descriptionWithAppName() {
+ LabeledIntent labeledIntent = new LabeledIntent(
+ TITLE_WITHOUT_ENTITY,
+ TITLE_WITH_ENTITY,
+ DESCRIPTION,
+ DESCRIPTION_WITH_APP_NAME,
+ INTENT,
+ REQUEST_CODE
+ );
+
+ LabeledIntent.Result result = labeledIntent.resolve(
+ mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
+
+ assertThat(result).isNotNull();
+ assertThat(result.remoteAction.getContentDescription()).isEqualTo("Use fake to open map");
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
index 2e97e63..b9a1a8c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
@@ -50,6 +50,7 @@
private static final String TEXT = "text";
private static final String TITLE_WITHOUT_ENTITY = "Map";
private static final String DESCRIPTION = "Opens in Maps";
+ private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open Map";
private static final String ACTION = Intent.ACTION_VIEW;
@Mock
@@ -219,6 +220,7 @@
TITLE_WITHOUT_ENTITY,
null,
DESCRIPTION,
+ DESCRIPTION_WITH_APP_NAME,
ACTION,
null,
null,
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
index 6e3de2d..a33c358 100644
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
@@ -41,6 +41,7 @@
private static final String TITLE_WITHOUT_ENTITY = "Map";
private static final String TITLE_WITH_ENTITY = "Map NW14D1";
private static final String DESCRIPTION = "Check the map";
+ private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
private static final String ACTION = Intent.ACTION_VIEW;
private static final String DATA = Uri.parse("http://www.android.com").toString();
private static final String TYPE = "text/html";
@@ -73,6 +74,7 @@
TITLE_WITHOUT_ENTITY,
TITLE_WITH_ENTITY,
DESCRIPTION,
+ DESCRIPTION_WITH_APP_NAME,
ACTION,
DATA,
TYPE,
@@ -91,6 +93,7 @@
assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
assertThat(labeledIntent.titleWithEntity).isEqualTo(TITLE_WITH_ENTITY);
assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
+ assertThat(labeledIntent.descriptionWithAppName).isEqualTo(DESCRIPTION_WITH_APP_NAME);
assertThat(labeledIntent.requestCode).isEqualTo(REQUEST_CODE);
Intent intent = labeledIntent.intent;
assertThat(intent.getAction()).isEqualTo(ACTION);
@@ -109,6 +112,7 @@
TITLE_WITHOUT_ENTITY,
TITLE_WITH_ENTITY,
DESCRIPTION,
+ DESCRIPTION_WITH_APP_NAME,
ACTION,
"HTTp://www.android.com",
TYPE,
@@ -132,6 +136,7 @@
TITLE_WITHOUT_ENTITY,
null,
DESCRIPTION,
+ null,
ACTION,
null,
null,
@@ -177,6 +182,7 @@
TITLE_WITHOUT_ENTITY,
TITLE_WITH_ENTITY,
DESCRIPTION,
+ DESCRIPTION_WITH_APP_NAME,
ACTION,
DATA,
TYPE,
@@ -199,6 +205,7 @@
null,
null,
DESCRIPTION,
+ DESCRIPTION_WITH_APP_NAME,
ACTION,
null,
null,
@@ -221,6 +228,7 @@
TITLE_WITHOUT_ENTITY,
TITLE_WITH_ENTITY,
null,
+ null,
ACTION,
null,
null,
@@ -243,6 +251,7 @@
TITLE_WITHOUT_ENTITY,
TITLE_WITH_ENTITY,
DESCRIPTION,
+ DESCRIPTION_WITH_APP_NAME,
null,
null,
null,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
index 460fe47..b45f8d2 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
@@ -26,8 +26,8 @@
import android.platform.test.annotations.Presubmit;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
@@ -210,6 +210,73 @@
assertThat(threadNames(processes2)).containsExactly("thread0");
}
+ @Test
+ public void test_nonNegativeOtherThreads() {
+ when(mMockReader.getProcessCpuUsage())
+ .thenReturn(createProcess(new int[] {0}, new int[] {0}))
+ .thenReturn(createProcess(new int[] {4}, new int[] {4}))
+ .thenReturn(createProcess(new int[] {10}, new int[] {7}))
+ .thenReturn(createProcess(new int[] {20}, new int[] {15}));
+ KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
+ new KernelCpuThreadReaderDiff(mMockReader, 5);
+ assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
+
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
+ kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
+ assertThat(cpuUsages(processes1)).containsExactly(Collections.singletonList(8));
+ assertThat(threadNames(processes1)).containsExactly("__OTHER_THREADS");
+
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes2 =
+ kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
+ assertThat(cpuUsages(processes2))
+ .containsExactly(Collections.singletonList(6), Collections.singletonList(3));
+ assertThat(threadNames(processes2)).containsExactly("thread0", "__OTHER_THREADS");
+
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes3 =
+ kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
+ assertThat(cpuUsages(processes3))
+ .containsExactly(Collections.singletonList(10), Collections.singletonList(8));
+ assertThat(threadNames(processes3)).containsExactly("thread0", "thread1");
+ }
+
+ @Test
+ public void test_otherThreadsOnZeroDiff() {
+ when(mMockReader.getProcessCpuUsage())
+ .thenReturn(createProcess(new int[] {0}))
+ .thenReturn(createProcess(new int[] {0}));
+ KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
+ new KernelCpuThreadReaderDiff(mMockReader, 5);
+ assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
+
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
+ kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
+ assertThat(cpuUsages(processes1)).containsExactly(Collections.singletonList(0));
+ assertThat(threadNames(processes1)).containsExactly("__OTHER_THREADS");
+ }
+
+ @Test
+ public void test_failureAndNewThread() {
+ when(mMockReader.getProcessCpuUsage())
+ .thenReturn(createProcess(new int[] {0}))
+ .thenThrow(new RuntimeException())
+ .thenReturn(createProcess(new int[] {1}, new int[] {10}))
+ .thenReturn(createProcess(new int[] {2}, new int[] {12}));
+ KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
+ new KernelCpuThreadReaderDiff(mMockReader, 0);
+ assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
+
+ assertThrows(
+ RuntimeException.class,
+ () -> cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()));
+ assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
+
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
+ kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
+ assertThat(cpuUsages(processes1))
+ .containsExactly(Collections.singletonList(1), Collections.singletonList(2));
+ assertThat(threadNames(processes1)).containsExactly("thread0", "thread1");
+ }
+
private ArrayList<KernelCpuThreadReader.ProcessCpuUsage> createProcess(
int[]... cpuUsageMillis) {
ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = new ArrayList<>();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index ae847c1..c3e4014 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -176,10 +176,10 @@
frequencies, 4);
assertArrayEquals(
new int[]{1, 3, 1, 3},
- frequencyBucketCreator.getBucketMinFrequencies(frequencies));
+ frequencyBucketCreator.bucketFrequencies(frequencies));
assertArrayEquals(
new int[]{2, 2, 2, 2},
- frequencyBucketCreator.getBucketedValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
+ frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
@@ -190,10 +190,10 @@
frequencies, 4);
assertArrayEquals(
new int[]{1, 3, 5, 7},
- frequencyBucketCreator.getBucketMinFrequencies(frequencies));
+ frequencyBucketCreator.bucketFrequencies(frequencies));
assertArrayEquals(
new int[]{2, 2, 2, 2},
- frequencyBucketCreator.getBucketedValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
+ frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
@@ -204,10 +204,10 @@
frequencies, 4);
assertArrayEquals(
new int[]{1, 3, 1, 2},
- frequencyBucketCreator.getBucketMinFrequencies(frequencies));
+ frequencyBucketCreator.bucketFrequencies(frequencies));
assertArrayEquals(
new int[]{2, 3, 1, 2},
- frequencyBucketCreator.getBucketedValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
+ frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
@@ -218,10 +218,10 @@
frequencies, 4);
assertArrayEquals(
new int[]{1, 2, 1, 3},
- frequencyBucketCreator.getBucketMinFrequencies(frequencies));
+ frequencyBucketCreator.bucketFrequencies(frequencies));
assertArrayEquals(
new int[]{1, 2, 2, 3},
- frequencyBucketCreator.getBucketedValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
+ frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
@@ -232,10 +232,10 @@
frequencies, 8);
assertArrayEquals(
new int[]{1, 2, 3, 4, 1, 2, 3, 4},
- frequencyBucketCreator.getBucketMinFrequencies(frequencies));
+ frequencyBucketCreator.bucketFrequencies(frequencies));
assertArrayEquals(
new int[]{1, 1, 1, 1, 1, 1, 1, 1},
- frequencyBucketCreator.getBucketedValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
+ frequencyBucketCreator.bucketValues(new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
@@ -246,10 +246,10 @@
frequencies, 8);
assertArrayEquals(
new int[]{1, 3, 5, 7, 1, 2, 3},
- frequencyBucketCreator.getBucketMinFrequencies(frequencies));
+ frequencyBucketCreator.bucketFrequencies(frequencies));
assertArrayEquals(
new int[]{2, 2, 2, 3, 1, 1, 1},
- frequencyBucketCreator.getBucketedValues(
+ frequencyBucketCreator.bucketValues(
new long[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}));
}
@@ -261,39 +261,37 @@
frequencies, 1);
assertArrayEquals(
new int[]{1},
- frequencyBucketCreator.getBucketMinFrequencies(frequencies));
+ frequencyBucketCreator.bucketFrequencies(frequencies));
assertArrayEquals(
new int[]{8},
- frequencyBucketCreator.getBucketedValues(
+ frequencyBucketCreator.bucketValues(
new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
- public void testGetBigFrequenciesStartIndex_simple() {
- assertEquals(
- 3, KernelCpuThreadReader.FrequencyBucketCreator.getBigFrequenciesStartIndex(
- new long[]{1, 2, 3, 1, 2, 3}));
+ public void testBucketSetup_threeClusters() {
+ long[] frequencies = {1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6};
+ KernelCpuThreadReader.FrequencyBucketCreator frequencyBucketCreator =
+ new KernelCpuThreadReader.FrequencyBucketCreator(frequencies, 6);
+ assertArrayEquals(
+ new int[] {1, 3, 2, 4, 3, 5},
+ frequencyBucketCreator.bucketFrequencies(frequencies));
+ assertArrayEquals(
+ new int[] {2, 2, 2, 2, 2, 2},
+ frequencyBucketCreator.bucketValues(
+ new long[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
- public void testGetBigFrequenciesStartIndex_moreLittle() {
- assertEquals(
- 4, KernelCpuThreadReader.FrequencyBucketCreator.getBigFrequenciesStartIndex(
- new long[]{1, 2, 3, 4, 1, 2}));
- }
-
- @Test
- public void testGetBigFrequenciesStartIndex_moreBig() {
- assertEquals(
- 2, KernelCpuThreadReader.FrequencyBucketCreator.getBigFrequenciesStartIndex(
- new long[]{1, 2, 1, 2, 3, 4}));
- }
-
- @Test
- public void testGetBigFrequenciesStartIndex_noBig() {
- assertEquals(
- 4, KernelCpuThreadReader.FrequencyBucketCreator.getBigFrequenciesStartIndex(
- new long[]{1, 2, 3, 4}));
+ public void testBucketSetup_moreClustersThanBuckets() {
+ long[] frequencies = {1, 1, 1, 1, 1, 1, 1, 1};
+ KernelCpuThreadReader.FrequencyBucketCreator frequencyBucketCreator =
+ new KernelCpuThreadReader.FrequencyBucketCreator(frequencies, 4);
+ assertArrayEquals(
+ new int[] {1, 1, 1, 1}, frequencyBucketCreator.bucketFrequencies(frequencies));
+ assertArrayEquals(
+ new int[] {1, 1, 1, 5},
+ frequencyBucketCreator.bucketValues(new long[] {1, 1, 1, 1, 1, 1, 1, 1}));
}
@Test
diff --git a/core/tests/featureflagtests/Android.bp b/core/tests/featureflagtests/Android.bp
new file mode 100644
index 0000000..8730b70
--- /dev/null
+++ b/core/tests/featureflagtests/Android.bp
@@ -0,0 +1,19 @@
+android_test {
+ name: "FrameworksCoreFeatureFlagTests",
+ // We only want this apk build for tests.
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+ dxflags: ["--core-library"],
+ static_libs: [
+ "android-common",
+ "frameworks-core-util-lib",
+ "androidx.test.rules",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/featureflagtests/Android.mk b/core/tests/featureflagtests/Android.mk
deleted file mode 100644
index ce7cb18..0000000
--- a/core/tests/featureflagtests/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib androidx.test.rules
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_PACKAGE_NAME := FrameworksCoreFeatureFlagTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
index 97a3d00..fa15241 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
@@ -19,7 +19,6 @@
LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayOne
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_FLAGS := --no-resource-removal
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
index 8ac6953..7d28408 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
@@ -19,5 +19,5 @@
android:versionCode="1"
android:versionName="1.0">
<application android:hasCode="false" />
- <overlay android:targetPackage="com.android.overlaytest" android:priority="1" />
+ <overlay android:targetPackage="com.android.overlaytest" />
</manifest>
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
index a347025..ada9b3c 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
@@ -19,7 +19,6 @@
LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayTwo
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_FLAGS := --no-resource-removal
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml
index f3c39cc..6e75a350 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml
@@ -19,5 +19,5 @@
android:versionCode="1"
android:versionName="1.0">
<application android:hasCode="false" />
- <overlay android:targetPackage="com.android.overlaytest" android:priority="2" />
+ <overlay android:targetPackage="com.android.overlaytest" />
</manifest>
diff --git a/core/tests/overlaytests/host/Android.mk b/core/tests/overlaytests/host/Android.mk
index b48a46b..e7348d5 100644
--- a/core/tests/overlaytests/host/Android.mk
+++ b/core/tests/overlaytests/host/Android.mk
@@ -21,7 +21,7 @@
LOCAL_JAVA_LIBRARIES := tradefed
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_TARGET_REQUIRED_MODULES := \
- OverlayHostTests_BadSignatureOverlay \
+ OverlayHostTests_NonPlatformSignatureOverlay \
OverlayHostTests_PlatformSignatureStaticOverlay \
OverlayHostTests_PlatformSignatureOverlay \
OverlayHostTests_UpdateOverlay \
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index f9672d2..99b6421 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -67,10 +67,10 @@
}
@Test
- public void failToInstallNonPlatformSignedOverlay() throws Exception {
+ public void failToInstallNonPlatformSignedOverlayTargetPreQ() throws Exception {
try {
- installPackage("OverlayHostTests_BadSignatureOverlay.apk");
- fail("installed a non-platform signed overlay");
+ installPackage("OverlayHostTests_NonPlatformSignatureOverlay.apk");
+ fail("installed a non-platform signed overlay with targetSdkVersion < Q");
} catch (Exception e) {
// Expected.
}
@@ -155,9 +155,17 @@
}
}
+ @Test
+ public void instantAppsNotVisibleToOMS() throws Exception {
+ installInstantPackage("OverlayHostTests_AppOverlayV1.apk");
+ assertFalse(overlayManagerContainsPackage(APP_OVERLAY_PACKAGE_NAME));
+ installConvertExistingInstantPackageToFull(APP_OVERLAY_PACKAGE_NAME);
+ assertTrue(overlayManagerContainsPackage(APP_OVERLAY_PACKAGE_NAME));
+ }
+
private void delay() {
try {
- Thread.sleep(100);
+ Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
@@ -167,6 +175,15 @@
delay();
}
+ private void installInstantPackage(String pkg) throws Exception {
+ super.installPackage(pkg, "--instant");
+ delay();
+ }
+
+ private void installConvertExistingInstantPackageToFull(String pkg) throws Exception {
+ getDevice().executeShellCommand("cmd package install-existing --wait --full " + pkg);
+ }
+
private void setPackageEnabled(String pkg, boolean enabled) throws Exception {
getDevice().executeShellCommand("cmd package " + (enabled ? "enable " : "disable ") + pkg);
delay();
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
index 3d2410d..cc7704b 100644
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_BadSignatureOverlay
+LOCAL_PACKAGE_NAME := OverlayHostTests_NonPlatformSignatureOverlay
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_bad
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
index 26b3875..67592f8 100644
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.om.hosttest.signature_overlay">
+ <uses-sdk android:targetSdkVersion="28" />
<application android:hasCode="false" />
<overlay android:targetPackage="android" />
</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
index c7b2dd1..f8607f4 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -58,7 +58,6 @@
LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v1/res
@@ -70,7 +69,6 @@
LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v2/res
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/AndroidManifest.xml
index f1a3981..b6ff0c3 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/AndroidManifest.xml
@@ -17,6 +17,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.om.hosttest.app_overlay">
<application android:hasCode="false" />
- <overlay android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
- android:category="android.theme" />
+ <overlay android:targetPackage="com.android.server.om.hosttest.update_overlay_test" />
</manifest>
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 365be10..21609d3 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -75,6 +75,7 @@
const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay";
const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
+const char* AssetManager::ODM_OVERLAY_DIR = "/odm/overlay";
const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
const char* AssetManager::TARGET_PACKAGE_NAME = "android";
const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index a99e77f..e1067fc 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -49,15 +49,21 @@
int ashmemFd = ashmem_create_region(ashmemName.string(), size);
if (ashmemFd < 0) {
result = -errno;
+ ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno);
} else {
result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
- if (result >= 0) {
+ if (result < 0) {
+ ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno);
+ } else {
void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
if (data == MAP_FAILED) {
result = -errno;
+ ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
} else {
result = ashmem_set_prot_region(ashmemFd, PROT_READ);
- if (result >= 0) {
+ if (result < 0) {
+ ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno);
+ } else {
CursorWindow* window = new CursorWindow(name, ashmemFd,
data, size, false /*readOnly*/);
result = window->clear();
@@ -86,26 +92,34 @@
String8 name = parcel->readString8();
status_t result;
+ int actualSize;
int ashmemFd = parcel->readFileDescriptor();
if (ashmemFd == int(BAD_TYPE)) {
result = BAD_TYPE;
+ ALOGE("CursorWindow: readFileDescriptor() failed");
} else {
ssize_t size = ashmem_get_size_region(ashmemFd);
if (size < 0) {
result = UNKNOWN_ERROR;
+ ALOGE("CursorWindow: ashmem_get_size_region() failed: errno=%d.", errno);
} else {
int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0);
if (dupAshmemFd < 0) {
result = -errno;
+ ALOGE("CursorWindow: fcntl() failed: errno=%d.", errno);
} else {
// the size of the ashmem descriptor can be modified between ashmem_get_size_region
// call and mmap, so we'll check again immediately after memory is mapped
void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
if (data == MAP_FAILED) {
result = -errno;
- } else if (ashmem_get_size_region(dupAshmemFd) != size) {
+ ALOGE("CursorWindow: mmap() failed: errno=%d.", errno);
+ } else if ((actualSize = ashmem_get_size_region(dupAshmemFd)) != size) {
::munmap(data, size);
result = BAD_VALUE;
+ ALOGE("CursorWindow: ashmem_get_size_region() returned %d, expected %d"
+ " errno=%d",
+ actualSize, (int) size, errno);
} else {
CursorWindow* window = new CursorWindow(name, dupAshmemFd,
data, size, true /*readOnly*/);
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index e22e2d2..a015eab 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -62,6 +62,7 @@
static const char* VENDOR_OVERLAY_DIR;
static const char* PRODUCT_OVERLAY_DIR;
static const char* PRODUCT_SERVICES_OVERLAY_DIR;
+ static const char* ODM_OVERLAY_DIR;
/*
* If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
* APKs in VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bf1063d..55f1911 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -388,9 +388,10 @@
*/
public static final int FLAG_NO_SYSTEM_CAPTURE = 0x1 << 12;
- private final static int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO |
- FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY |
- FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER | FLAG_MUTE_HAPTIC;
+ private static final int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO
+ | FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY
+ | FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER | FLAG_NO_MEDIA_PROJECTION
+ | FLAG_MUTE_HAPTIC | FLAG_NO_SYSTEM_CAPTURE;
private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED |
FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY;
@@ -557,7 +558,7 @@
private int mContentType = CONTENT_TYPE_UNKNOWN;
private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
private int mFlags = 0x0;
- private boolean mMuteHapticChannels = true;
+ private boolean mMuteHapticChannels = false;
private HashSet<String> mTags = new HashSet<String>();
private Bundle mBundle;
@@ -888,7 +889,7 @@
/**
* Specifying if haptic should be muted or not when playing audio-haptic coupled data.
- * By default, haptic channels are disabled.
+ * By default, haptic channels are enabled.
* @param muted true to force muting haptic channels.
* @return the same Builder instance.
*/
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 5d12c3c..b2ebfa9 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -43,8 +43,6 @@
/** @hide */
public static final int PLAYER_PIID_INVALID = -1;
/** @hide */
- public static final int PLAYER_PIID_UNASSIGNED = 0;
- /** @hide */
public static final int PLAYER_UPID_INVALID = -1;
// information about the implementation
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 50caf733..ee8f1b3 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -50,6 +50,10 @@
private static final boolean DEBUG = DEBUG_APP_OPS || false;
private static IAudioService sService; //lazy initialization, use getService()
+ /** if true, only use OP_PLAY_AUDIO monitoring for logging, and rely on muting to happen
+ * in AudioFlinger */
+ private static final boolean USE_AUDIOFLINGER_MUTING_FOR_OP = true;
+
// parameters of the player that affect AppOps
protected AudioAttributes mAttributes;
@@ -67,13 +71,13 @@
// for AppOps
private @Nullable IAppOpsService mAppOps;
- private IAppOpsCallback mAppOpsCallback;
+ private @Nullable IAppOpsCallback mAppOpsCallback;
@GuardedBy("mLock")
private boolean mHasAppOpsPlayAudio = true;
private final int mImplType;
// uniquely identifies the Player Interface throughout the system (P I Id)
- private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_UNASSIGNED;
+ private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
@GuardedBy("mLock")
private int mState;
@@ -104,27 +108,27 @@
* Call from derived class when instantiation / initialization is successful
*/
protected void baseRegisterPlayer() {
- int newPiid = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
- IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
- mAppOps = IAppOpsService.Stub.asInterface(b);
- // initialize mHasAppOpsPlayAudio
- updateAppOpsPlayAudio();
- // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
- mAppOpsCallback = new IAppOpsCallbackWrapper(this);
- try {
- mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
- ActivityThread.currentPackageName(), mAppOpsCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "Error registering appOps callback", e);
- mHasAppOpsPlayAudio = false;
+ if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
+ // initialize mHasAppOpsPlayAudio
+ updateAppOpsPlayAudio();
+ // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
+ mAppOpsCallback = new IAppOpsCallbackWrapper(this);
+ try {
+ mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
+ ActivityThread.currentPackageName(), mAppOpsCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error registering appOps callback", e);
+ mHasAppOpsPlayAudio = false;
+ }
}
try {
- newPiid = getService().trackPlayer(
+ mPlayerIId = getService().trackPlayer(
new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
}
- mPlayerIId = newPiid;
}
/**
@@ -284,6 +288,9 @@
* Must be called synchronized on mLock.
*/
void updateAppOpsPlayAudio_sync(boolean attributesChanged) {
+ if (USE_AUDIOFLINGER_MUTING_FOR_OP) {
+ return;
+ }
boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
try {
int mode = AppOpsManager.MODE_IGNORED;
@@ -333,6 +340,9 @@
* @return
*/
boolean isRestricted_sync() {
+ if (USE_AUDIOFLINGER_MUTING_FOR_OP) {
+ return false;
+ }
// check app ops
if (mHasAppOpsPlayAudio) {
return false;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 65b58ab..5c3d780 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -129,6 +129,7 @@
// NDK or LLNDK or NDK-compliant
"libandroid",
"libbinder_ndk",
+ "libcgrouprc",
"libmediandk",
"libmediametrics",
"libnativehelper_compat_libc++",
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 1f2480b..177f2b8 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -248,16 +248,21 @@
ASystemFontIterator_open; # introduced=29
ASystemFontIterator_close; # introduced=29
ASystemFontIterator_next; # introduced=29
- ASystemFont_close; # introduced=29
- ASystemFont_getFontFilePath; # introduced=29
- ASystemFont_getWeight; # introduced=29
- ASystemFont_isItalic; # introduced=29
- ASystemFont_getLocale; # introduced=29
- ASystemFont_getCollectionIndex; # introduced=29
- ASystemFont_getAxisCount; # introduced=29
- ASystemFont_getAxisTag; # introduced=29
- ASystemFont_getAxisValue; # introduced=29
- ASystemFont_matchFamilyStyleCharacter; # introduced=29
+ AFont_close; # introduced=29
+ AFont_getFontFilePath; # introduced=29
+ AFont_getWeight; # introduced=29
+ AFont_isItalic; # introduced=29
+ AFont_getLocale; # introduced=29
+ AFont_getCollectionIndex; # introduced=29
+ AFont_getAxisCount; # introduced=29
+ AFont_getAxisTag; # introduced=29
+ AFont_getAxisValue; # introduced=29
+ AFontMatcher_create; # introduced=29
+ AFontMatcher_destroy; # introduced=29
+ AFontMatcher_setStyle; # introduced=29
+ AFontMatcher_setLocales; # introduced=29
+ AFontMatcher_setFamilyVariant; # introduced=29
+ AFontMatcher_match; # introduced=29
ATrace_beginSection; # introduced=23
ATrace_endSection; # introduced=23
ATrace_isEnabled; # introduced=23
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 4d3d1d6..302cbd1 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -16,6 +16,8 @@
#include <jni.h>
+#include <android/font.h>
+#include <android/font_matcher.h>
#include <android/system_fonts.h>
#include <memory>
@@ -53,7 +55,7 @@
XmlDocUniquePtr mCustomizationXmlDoc;
};
-struct ASystemFont {
+struct AFont {
std::string mFilePath;
std::unique_ptr<std::string> mLocale;
uint16_t mWeight;
@@ -62,6 +64,19 @@
std::vector<std::pair<uint32_t, float>> mAxes;
};
+struct AFontMatcher {
+ minikin::FontStyle mFontStyle;
+ uint32_t mLocaleListId = 0; // 0 is reserved for empty locale ID.
+ bool mFamilyVariant = AFAMILY_VARIANT_DEFAULT;
+};
+
+static_assert(static_cast<uint32_t>(AFAMILY_VARIANT_DEFAULT) ==
+ static_cast<uint32_t>(minikin::FamilyVariant::DEFAULT));
+static_assert(static_cast<uint32_t>(AFAMILY_VARIANT_COMPACT) ==
+ static_cast<uint32_t>(minikin::FamilyVariant::COMPACT));
+static_assert(static_cast<uint32_t>(AFAMILY_VARIANT_ELEGANT) ==
+ static_cast<uint32_t>(minikin::FamilyVariant::ELEGANT));
+
namespace {
std::string xmlTrim(const std::string& in) {
@@ -101,7 +116,7 @@
return nullptr;
}
-void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, ASystemFont* out,
+void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, AFont* out,
const std::string& pathPrefix) {
const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang");
XmlCharUniquePtr filePathStr(
@@ -197,24 +212,48 @@
delete ite;
}
-ASystemFont* ASystemFont_matchFamilyStyleCharacter(
- const char* _Nonnull familyName,
+AFontMatcher* _Nonnull AFontMatcher_create() {
+ return new AFontMatcher();
+}
+
+void AFontMatcher_destroy(AFontMatcher* matcher) {
+ delete matcher;
+}
+
+void AFontMatcher_setStyle(
+ AFontMatcher* _Nonnull matcher,
uint16_t weight,
- bool italic,
- const char* _Nonnull languageTags,
+ bool italic) {
+ matcher->mFontStyle = minikin::FontStyle(
+ weight, static_cast<minikin::FontStyle::Slant>(italic));
+}
+
+void AFontMatcher_setLocales(
+ AFontMatcher* _Nonnull matcher,
+ const char* _Nonnull languageTags) {
+ matcher->mLocaleListId = minikin::registerLocaleList(languageTags);
+}
+
+void AFontMatcher_setFamilyVariant(AFontMatcher* _Nonnull matcher, uint32_t familyVariant) {
+ matcher->mFamilyVariant = familyVariant;
+}
+
+AFont* _Nonnull AFontMatcher_match(
+ const AFontMatcher* _Nonnull matcher,
+ const char* _Nonnull familyName,
const uint16_t* _Nonnull text,
- uint32_t textLength,
+ const uint32_t textLength,
uint32_t* _Nullable runLength) {
std::shared_ptr<minikin::FontCollection> fc =
minikin::SystemFonts::findFontCollection(familyName);
- std::vector<minikin::FontCollection::Run> runs =
- fc->itemize(minikin::U16StringPiece(text, textLength),
- minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic)),
- minikin::registerLocaleList(languageTags),
- minikin::FamilyVariant::DEFAULT);
+ std::vector<minikin::FontCollection::Run> runs = fc->itemize(
+ minikin::U16StringPiece(text, textLength),
+ matcher->mFontStyle,
+ matcher->mLocaleListId,
+ static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant));
const minikin::Font* font = runs[0].fakedFont.font;
- std::unique_ptr<ASystemFont> result = std::make_unique<ASystemFont>();
+ std::unique_ptr<AFont> result = std::make_unique<AFont>();
const android::MinikinFontSkia* minikinFontSkia =
reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
result->mFilePath = minikinFontSkia->getFilePath();
@@ -253,7 +292,7 @@
}
}
-ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
+AFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
if (ite->mXmlDoc) {
ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode);
@@ -262,7 +301,7 @@
ite->mXmlDoc.reset();
ite->mFontNode = nullptr;
} else {
- std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
+ std::unique_ptr<AFont> font = std::make_unique<AFont>();
copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/");
if (!isFontFileAvailable(font->mFilePath)) {
return ASystemFontIterator_next(ite);
@@ -279,7 +318,7 @@
ite->mFontNode = nullptr;
return nullptr;
} else {
- std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
+ std::unique_ptr<AFont> font = std::make_unique<AFont>();
copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/");
if (!isFontFileAvailable(font->mFilePath)) {
return ASystemFontIterator_next(ite);
@@ -290,48 +329,48 @@
return nullptr;
}
-void ASystemFont_close(ASystemFont* font) {
+void AFont_close(AFont* font) {
delete font;
}
-const char* ASystemFont_getFontFilePath(const ASystemFont* font) {
+const char* AFont_getFontFilePath(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
return font->mFilePath.c_str();
}
-uint16_t ASystemFont_getWeight(const ASystemFont* font) {
+uint16_t AFont_getWeight(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
return font->mWeight;
}
-bool ASystemFont_isItalic(const ASystemFont* font) {
+bool AFont_isItalic(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
return font->mItalic;
}
-const char* ASystemFont_getLocale(const ASystemFont* font) {
+const char* AFont_getLocale(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
return font->mLocale ? nullptr : font->mLocale->c_str();
}
-size_t ASystemFont_getCollectionIndex(const ASystemFont* font) {
+size_t AFont_getCollectionIndex(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
return font->mCollectionIndex;
}
-size_t ASystemFont_getAxisCount(const ASystemFont* font) {
+size_t AFont_getAxisCount(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
return font->mAxes.size();
}
-uint32_t ASystemFont_getAxisTag(const ASystemFont* font, uint32_t axisIndex) {
+uint32_t AFont_getAxisTag(const AFont* font, uint32_t axisIndex) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
"given axis index is out of bounds. (< %zd", font->mAxes.size());
return font->mAxes[axisIndex].first;
}
-float ASystemFont_getAxisValue(const ASystemFont* font, uint32_t axisIndex) {
+float AFont_getAxisValue(const AFont* font, uint32_t axisIndex) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
"given axis index is out of bounds. (< %zd", font->mAxes.size());
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
index bc614e9..a345091 100644
--- a/packages/CaptivePortalLogin/Android.bp
+++ b/packages/CaptivePortalLogin/Android.bp
@@ -23,6 +23,7 @@
static_libs: [
"androidx.legacy_legacy-support-v4",
"metrics-constants-protos",
+ "captiveportal-lib",
],
manifest: "AndroidManifest.xml",
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
index 38576ee..ea7b378 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
@@ -19,8 +19,10 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.DynamicSystemClient;
+import android.util.FeatureFlagUtils;
import android.util.Log;
@@ -35,6 +37,10 @@
@Override
public void onReceive(Context context, Intent intent) {
+ if (!featureFlagEnabled()) {
+ return;
+ }
+
String action = intent.getAction();
Log.d(TAG, "Broadcast received: " + action);
@@ -47,4 +53,9 @@
context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM);
}
}
+
+ private boolean featureFlagEnabled() {
+ return SystemProperties.getBoolean(
+ FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
+ }
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
index 2ad72eb..5c6885a 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
@@ -257,7 +257,7 @@
return;
}
- if (getStatus() != STATUS_READY) {
+ if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
Log.e(TAG, "Trying to discard AOT while there is no complete installation");
return;
}
@@ -273,13 +273,25 @@
}
private void executeRebootToDynSystemCommand() {
- if (mInstallTask == null || mInstallTask.getStatus() != FINISHED) {
+ boolean enabled = false;
+
+ if (mInstallTask != null && mInstallTask.getStatus() == FINISHED) {
+ enabled = mInstallTask.commit();
+ } else if (isDynamicSystemInstalled()) {
+ enabled = mDynSystem.setEnable(true);
+ } else {
Log.e(TAG, "Trying to reboot to AOT while there is no complete installation");
return;
}
- if (!mInstallTask.commit()) {
- Log.e(TAG, "Failed to commit installation because of native runtime error.");
+ if (enabled) {
+ PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+ if (powerManager != null) {
+ powerManager.reboot("dynsystem");
+ }
+ } else {
+ Log.e(TAG, "Failed to enable DynamicSystem because of native runtime error.");
mNM.cancel(NOTIFICATION_ID);
Toast.makeText(this,
@@ -287,14 +299,6 @@
Toast.LENGTH_LONG).show();
mDynSystem.remove();
-
- return;
- }
-
- PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
-
- if (powerManager != null) {
- powerManager.reboot("dynsystem");
}
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/VerificationActivity.java
index 269645d..b1c09381 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/VerificationActivity.java
@@ -25,8 +25,10 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.DynamicSystemClient;
+import android.util.FeatureFlagUtils;
import android.util.Log;
@@ -49,6 +51,12 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (!featureFlagEnabled()) {
+ Log.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; activity aborted.");
+ finish();
+ return;
+ }
+
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (km != null) {
@@ -98,6 +106,11 @@
startServiceAsUser(intent, UserHandle.SYSTEM);
}
+ private boolean featureFlagEnabled() {
+ return SystemProperties.getBoolean(
+ FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
+ }
+
static boolean isVerified(String url) {
return sVerifiedUrl != null && sVerifiedUrl.equals(url);
}
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index d56f97f..57a3db5 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+java_library {
+ name: "captiveportal-lib",
+ srcs: ["common/**/*.java"],
+ libs: [
+ "androidx.annotation_annotation",
+ ],
+ sdk_version: "system_current",
+}
+
java_defaults {
name: "NetworkStackCommon",
sdk_version: "system_current",
@@ -35,6 +44,7 @@
"networkstack-aidl-interfaces-java",
"datastallprotosnano",
"networkstackprotosnano",
+ "captiveportal-lib",
],
manifest: "AndroidManifestBase.xml",
}
diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java b/packages/NetworkStack/common/CaptivePortalProbeResult.java
similarity index 94%
rename from core/java/android/net/captiveportal/CaptivePortalProbeResult.java
rename to packages/NetworkStack/common/CaptivePortalProbeResult.java
index a1d3de2..48cd48b 100644
--- a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java
+++ b/packages/NetworkStack/common/CaptivePortalProbeResult.java
@@ -16,17 +16,13 @@
package android.net.captiveportal;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
* Result of calling isCaptivePortal().
* @hide
*/
-@SystemApi
-@TestApi
public final class CaptivePortalProbeResult {
public static final int SUCCESS_CODE = 204;
public static final int FAILED_CODE = 599;
diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java b/packages/NetworkStack/common/CaptivePortalProbeSpec.java
similarity index 95%
rename from core/java/android/net/captiveportal/CaptivePortalProbeSpec.java
rename to packages/NetworkStack/common/CaptivePortalProbeSpec.java
index b354607..bf983a5 100644
--- a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java
+++ b/packages/NetworkStack/common/CaptivePortalProbeSpec.java
@@ -19,16 +19,12 @@
import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE;
import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import java.net.MalformedURLException;
import java.net.URL;
@@ -40,8 +36,6 @@
import java.util.regex.PatternSyntaxException;
/** @hide */
-@SystemApi
-@TestApi
public abstract class CaptivePortalProbeSpec {
private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName();
private static final String REGEX_SEPARATOR = "@@/@@";
@@ -192,4 +186,10 @@
// No value is a match ("no location header" passes the location rule for non-redirects)
return pattern == null || TextUtils.isEmpty(value) || pattern.matcher(value).matches();
}
+
+ // Throws NullPointerException if the input is null.
+ private static <T> T checkNotNull(T object) {
+ if (object == null) throw new NullPointerException();
+ return object;
+ }
}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
index 5650f21..bee4bbd9 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
@@ -33,8 +33,8 @@
import android.net.ipmemorystore.Blob;
import android.net.ipmemorystore.IOnBlobRetrievedListener;
import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
import android.net.ipmemorystore.IOnStatusListener;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
@@ -297,16 +297,16 @@
*/
@Override
public void isSameNetwork(@Nullable final String l2Key1, @Nullable final String l2Key2,
- @Nullable final IOnSameNetworkResponseListener listener) {
+ @Nullable final IOnSameL3NetworkResponseListener listener) {
if (null == listener) return;
mExecutor.execute(() -> {
try {
if (null == l2Key1 || null == l2Key2) {
- listener.onSameNetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+ listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
return;
}
if (null == mDb) {
- listener.onSameNetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+ listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
return;
}
try {
@@ -315,16 +315,16 @@
final NetworkAttributes attr2 =
IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key2);
if (null == attr1 || null == attr2) {
- listener.onSameNetworkResponse(makeStatus(SUCCESS),
+ listener.onSameL3NetworkResponse(makeStatus(SUCCESS),
new SameL3NetworkResponse(l2Key1, l2Key2,
-1f /* never connected */).toParcelable());
return;
}
final float confidence = attr1.getNetworkGroupSamenessConfidence(attr2);
- listener.onSameNetworkResponse(makeStatus(SUCCESS),
+ listener.onSameL3NetworkResponse(makeStatus(SUCCESS),
new SameL3NetworkResponse(l2Key1, l2Key2, confidence).toParcelable());
} catch (Exception e) {
- listener.onSameNetworkResponse(makeStatus(ERROR_GENERIC), null);
+ listener.onSameL3NetworkResponse(makeStatus(ERROR_GENERIC), null);
}
} catch (final RemoteException e) {
// Client at the other end died
@@ -343,7 +343,7 @@
*/
@Override
public void retrieveNetworkAttributes(@Nullable final String l2Key,
- @Nullable final IOnNetworkAttributesRetrieved listener) {
+ @Nullable final IOnNetworkAttributesRetrievedListener listener) {
if (null == listener) return;
mExecutor.execute(() -> {
try {
diff --git a/tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java b/packages/NetworkStack/tests/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java
similarity index 100%
rename from tests/net/java/android/net/captiveportal/CaptivePortalProbeSpecTest.java
rename to packages/NetworkStack/tests/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
index 94cc589..a00eff7 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -31,8 +31,8 @@
import android.net.ipmemorystore.Blob;
import android.net.ipmemorystore.IOnBlobRetrievedListener;
import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
import android.net.ipmemorystore.IOnStatusListener;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
@@ -163,9 +163,9 @@
private interface OnNetworkAttributesRetrievedListener {
void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attr);
}
- private IOnNetworkAttributesRetrieved onNetworkAttributesRetrieved(
+ private IOnNetworkAttributesRetrievedListener onNetworkAttributesRetrieved(
final OnNetworkAttributesRetrievedListener functor) {
- return new IOnNetworkAttributesRetrieved() {
+ return new IOnNetworkAttributesRetrievedListener() {
@Override
public void onNetworkAttributesRetrieved(final StatusParcelable status,
final String l2Key, final NetworkAttributesParcelable attributes)
@@ -182,17 +182,17 @@
}
/** Helper method to make an IOnSameNetworkResponseListener */
- private interface OnSameNetworkResponseListener {
- void onSameNetworkResponse(Status status, SameL3NetworkResponse answer);
+ private interface OnSameL3NetworkResponseListener {
+ void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer);
}
- private IOnSameNetworkResponseListener onSameResponse(
- final OnSameNetworkResponseListener functor) {
- return new IOnSameNetworkResponseListener() {
+ private IOnSameL3NetworkResponseListener onSameResponse(
+ final OnSameL3NetworkResponseListener functor) {
+ return new IOnSameL3NetworkResponseListener() {
@Override
- public void onSameNetworkResponse(final StatusParcelable status,
+ public void onSameL3NetworkResponse(final StatusParcelable status,
final SameL3NetworkResponseParcelable sameL3Network)
throws RemoteException {
- functor.onSameNetworkResponse(new Status(status),
+ functor.onSameL3NetworkResponse(new Status(status),
null == sameL3Network ? null : new SameL3NetworkResponse(sameL3Network));
}
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
index 25246d6..0a336bf 100644
--- a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
@@ -36,6 +36,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginBottom="20dp"
android:gravity="center|bottom">
<com.android.settingslib.widget.BarView
diff --git a/packages/SettingsLib/BarChartPreference/res/values/dimens.xml b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
index 7148afa..5f741a0 100644
--- a/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
@@ -16,6 +16,6 @@
-->
<resources>
- <dimen name="settings_bar_view_max_height">106dp</dimen>
+ <dimen name="settings_bar_view_max_height">72dp</dimen>
<dimen name="settings_bar_view_icon_size">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
index 8302c01..7a3fb7d 100644
--- a/packages/SettingsLib/BarChartPreference/res/values/styles.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -40,7 +40,7 @@
<style name="SettingsBarViewStyle">
<item name="android:layout_width">0dp</item>
- <item name="android:layout_height">250dp</item>
+ <item name="android:layout_height">168dp</item>
<item name="android:layout_weight">1</item>
<item name="android:layout_marginStart">8dp</item>
<item name="android:layout_marginEnd">8dp</item>
@@ -63,6 +63,7 @@
<item name="android:layout_height">@dimen/settings_bar_view_icon_size</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:layout_marginTop">12dp</item>
+ <item name="android:tint">?android:attr/textColorPrimary</item>
</style>
<style name="SettingsBarChartBarTitle">
@@ -78,7 +79,6 @@
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">4dp</item>
- <item name="android:layout_marginBottom">12dp</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">marquee</item>
<item name="android:textAppearance">@style/BarChart.Text.Summary</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
index 9feacac..eeb6cb0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
@@ -75,20 +75,32 @@
return true;
}
+ if (isDefaultActiveApp(pkg)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if it is default active app in multiple area(i.e. SMS, Dialer, Device admin..)
+ */
+ public boolean isDefaultActiveApp(String pkg) {
// Additionally, check if pkg is default dialer/sms. They are considered essential apps and
// should be automatically whitelisted (otherwise user may be able to set restriction on
// them, leading to bad device behavior.)
- if (!mAppContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
- return false;
- }
+
+ final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY);
final ComponentName defaultSms = SmsApplication.getDefaultSmsApplication(mAppContext,
true /* updateIfNeeded */);
- if (defaultSms != null && TextUtils.equals(pkg, defaultSms.getPackageName())) {
+ if (hasTelephony && defaultSms != null && TextUtils.equals(pkg,
+ defaultSms.getPackageName())) {
return true;
}
final String defaultDialer = DefaultDialerManager.getDefaultDialerApplication(mAppContext);
- if (TextUtils.equals(pkg, defaultDialer)) {
+ if (hasTelephony && TextUtils.equals(pkg, defaultDialer)) {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
index b9197fe..66ee802 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
@@ -210,7 +210,6 @@
private void setupUi(ConditionTag tag, View row) {
if (tag.lines == null) {
tag.lines = row.findViewById(android.R.id.content);
- tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
}
if (tag.line1 == null) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
index bbf807d2..44ee423 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -118,6 +118,7 @@
ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(testSms, "receiver"));
assertThat(mPowerWhitelistBackend.isWhitelisted(testSms)).isTrue();
+ assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testSms)).isTrue();
}
@Test
@@ -126,6 +127,7 @@
ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer);
assertThat(mPowerWhitelistBackend.isWhitelisted(testDialer)).isTrue();
+ assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testDialer)).isTrue();
}
@Test
@@ -133,6 +135,7 @@
doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);
assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerWhitelistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
}
@Test
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index abd2b76..fcf9200 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -112,6 +112,7 @@
<uses-permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" />
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.MONITOR_INPUT" />
<!-- DreamManager -->
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 105be46..58d50ea 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -28,7 +28,7 @@
public interface ClockPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_CLOCK";
- int VERSION = 3;
+ int VERSION = 4;
/**
* Get the name of the clock face.
@@ -72,6 +72,14 @@
}
/**
+ * Allows the plugin to clean up resources when no longer needed.
+ *
+ * Called when the view previously created by {@link ClockPlugin#getView()} has been detached
+ * from the view hierarchy.
+ */
+ void onDestroyView();
+
+ /**
* Set clock paint style.
* @param style The new style to set in the paint.
*/
diff --git a/packages/SystemUI/res/drawable-nodpi/icon.xml b/packages/SystemUI/res/drawable-nodpi/icon.xml
index 48094c4..7a68c03 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon.xml
@@ -15,5 +15,5 @@
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/icon_bg"/>
- <foreground android:drawable="@drawable/p"/>
+ <foreground android:drawable="@drawable/q"/>
</adaptive-icon>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
index 31ecf7e..2a54dfa 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<color xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="#C5E1A5" />
+ android:color="#77C360" />
diff --git a/packages/SystemUI/res/drawable-nodpi/p.xml b/packages/SystemUI/res/drawable-nodpi/p.xml
deleted file mode 100644
index 596b782..0000000
--- a/packages/SystemUI/res/drawable-nodpi/p.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="108dp"
- android:height="108dp"
- android:viewportWidth="108"
- android:viewportHeight="108">
- <path
- android:pathData="M49,65L54,65C60.075,65 65,60.075 65,54C65,47.925 60.075,43 54,43C47.925,43 43,47.925 43,54L43,108"
- android:strokeWidth="16"
- android:fillColor="#00000000"
- android:strokeColor="#7CB342"
- android:fillType="evenOdd"/>
- <path
- android:pathData="M51,65L54,65C60.075,65 65,60.075 65,54C65,47.925 60.075,43 54,43C47.925,43 43,47.925 43,54L43,108"
- android:strokeWidth="8"
- android:fillColor="#00000000"
- android:strokeColor="#FFFFFF"
- android:fillType="evenOdd"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable-nodpi/q.xml b/packages/SystemUI/res/drawable-nodpi/q.xml
new file mode 100644
index 0000000..0f42d2e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/q.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108.0"
+ android:viewportHeight="108.0">
+ <group
+ android:name="scale"
+ android:pivotX="54" android:pivotY="54"
+ android:scaleX="0.9"
+ android:scaleY="0.9">
+ <group
+ android:name="nudge"
+ android:translateX="24"
+ android:translateY="23.5">
+ <path
+ android:name="tail"
+ android:fillColor="#FFFFFF"
+ android:pathData="M21.749674,34.122784l-9.431964,9.529709l-6.31771,-6.2529106l15.736504,-15.899582l64.765724,65.16436l-6.3046494,6.266083z"/>
+ <path
+ android:name="counter"
+ android:fillColor="#FFFFFF"
+ android:pathData="M30,9.32352941 C41.6954418,9.32352941 51.1764706,18.8045582 51.1764706,30.5 C51.1764706,42.1954418 41.6954418,51.6764706 30,51.6764706 C18.3045582,51.6764706 8.82352941,42.1954418 8.82352941,30.5 C8.82352941,18.8045582 18.3045582,9.32352941 30,9.32352941 L30,9.32352941 Z M30,0.5 C13.4314575,0.5 -5.53805368e-15,13.9314575 -7.10542736e-15,30.5 C-1.02401747e-14,47.0685425 13.4314575,60.5 30,60.5 C46.5685425,60.5 60,47.0685425 60,30.5 C59.9805514,13.9395201 46.5604799,0.519448617 30,0.5 Z"/>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/bubble_flyout.xml b/packages/SystemUI/res/drawable/bubble_flyout.xml
new file mode 100644
index 0000000..5406aaa
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bubble_flyout.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <!-- TODO: Add the triangle pointing to the bubble stack. -->
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius"
+ />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubble_flyout.xml b/packages/SystemUI/res/layout/bubble_flyout.xml
new file mode 100644
index 0000000..74c6c12
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_flyout.xml
@@ -0,0 +1,33 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bubble_flyout"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:background="@drawable/bubble_flyout"
+ android:padding="@dimen/bubble_flyout_padding"
+ android:translationZ="@dimen/bubble_flyout_elevation">
+
+ <TextView
+ android:id="@+id/bubble_flyout_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:maxWidth="@dimen/bubble_flyout_maxwidth"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubble_view.xml b/packages/SystemUI/res/layout/bubble_view.xml
index 13186fc..a8eb2914 100644
--- a/packages/SystemUI/res/layout/bubble_view.xml
+++ b/packages/SystemUI/res/layout/bubble_view.xml
@@ -27,12 +27,4 @@
android:padding="@dimen/bubble_view_padding"
android:clipToPadding="false"/>
- <TextView
- android:id="@+id/message_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minWidth="@dimen/bubble_message_min_width"
- android:maxWidth="@dimen/bubble_message_max_width"
- android:padding="@dimen/bubble_message_padding"/>
-
</com.android.systemui.bubbles.BubbleView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f8295eb..b0afe75 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -109,12 +109,12 @@
<!-- The default tiles to display in QuickSettings -->
<string name="quick_settings_tiles_default" translatable="false">
- wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,sensorprivacy
+ wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast
</string>
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,sensorprivacy
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6eb279a..bea0365 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -38,6 +38,14 @@
<dimen name="navigation_handle_sample_horizontal_margin">10dp</dimen>
<dimen name="navigation_home_handle_width">72dp</dimen>
+
+ <!-- Size of the nav bar edge panels, should be greater to the
+ edge sensitivity + the drag threshold -->
+ <dimen name="navigation_edge_panel_width">52dp</dimen>
+ <dimen name="navigation_edge_panel_height">52dp</dimen>
+ <!-- The threshold to drag to trigger the edge action -->
+ <dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
+
<!-- Luminance threshold to determine black/white contrast for the navigation affordances -->
<item name="navigation_luminance_threshold" type="dimen" format="float">0.5</item>
<!-- Luminance change threshold that allows applying new value if difference was exceeded -->
@@ -1027,6 +1035,12 @@
<!-- How much each bubble is elevated. -->
<dimen name="bubble_elevation">1dp</dimen>
+ <!-- How much the bubble flyout text container is elevated. -->
+ <dimen name="bubble_flyout_elevation">4dp</dimen>
+ <!-- How much padding is around the flyout text. -->
+ <dimen name="bubble_flyout_padding">16dp</dimen>
+ <!-- The maximum width of a bubble flyout. -->
+ <dimen name="bubble_flyout_maxwidth">200dp</dimen>
<!-- Padding around a collapsed bubble -->
<dimen name="bubble_view_padding">0dp</dimen>
<!-- Padding between bubbles when displayed in expanded state -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0411d01..cd286fc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -670,6 +670,9 @@
<item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> more notifications inside.</item>
</plurals>
+ <!-- Format to use to summarize a message from a contact in a single line of text. For example: "Julia: How's it going?". [CHAR LIMIT=NONE] -->
+ <string name="notification_summary_message_format"><xliff:g id="contact_name" example="Julia">%1$s</xliff:g>: <xliff:g id="message_content" example="How is it going?">%2$s</xliff:g></string>
+
<!-- Content description of button in notification inspector for system settings relating to
notifications from this application [CHAR LIMIT=NONE] -->
<string name="status_bar_notification_inspect_item_title">Notification settings</string>
@@ -2401,5 +2404,4 @@
<string name="bubble_accessibility_action_move_bottom_left">Move bottom left</string>
<!-- Action in accessibility menu to move the stack of bubbles to the bottom right of the screen. [CHAR LIMIT=30]-->
<string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string>
-
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index ce615b6..0075327 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -119,4 +119,8 @@
*/
void onAssistantAvailable(boolean available) = 13;
+ /**
+ * Sent when the assistant changes how visible it is to the user.
+ */
+ void onAssistantVisibilityChanged(float visibility) = 14;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 953816e..5764fe8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -92,4 +92,9 @@
* Start the assistant.
*/
void startAssistant(in Bundle bundle) = 13;
+
+ /**
+ * Creates a new gesture monitor
+ */
+ Bundle monitorGestureInput(String name, int displayId) = 14;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputMonitorCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputMonitorCompat.java
new file mode 100644
index 0000000..ddca723
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputMonitorCompat.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.system;
+
+import android.os.Bundle;
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.InputMonitor;
+
+import com.android.systemui.shared.system.InputChannelCompat.InputEventListener;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
+
+/**
+ * @see android.view.InputMonitor
+ */
+public class InputMonitorCompat {
+
+ private final InputMonitor mInputMonitor;
+
+ private InputMonitorCompat(InputMonitor monitor) {
+ mInputMonitor = monitor;
+ }
+
+ /**
+ * @see InputMonitor#pilferPointers()
+ */
+ public void pilferPointers() {
+ mInputMonitor.pilferPointers();
+ }
+
+ /**
+ * @see InputMonitor#dispose()
+ */
+ public void dispose() {
+ mInputMonitor.dispose();
+ }
+
+ /**
+ * @see InputMonitor#getInputChannel()
+ */
+ public InputEventReceiver getInputReceiver(Looper looper, Choreographer choreographer,
+ InputEventListener listener) {
+ return new InputEventReceiver(mInputMonitor.getInputChannel(), looper, choreographer,
+ listener);
+ }
+
+ /**
+ * Gets the input monitor stored in a bundle
+ */
+ public static InputMonitorCompat fromBundle(Bundle bundle, String key) {
+ return new InputMonitorCompat((InputMonitor) bundle.getParcelable(key));
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index d0c17b7..1076e73 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -24,6 +24,8 @@
import android.content.res.Resources;
import android.view.WindowManagerPolicyConstants;
+import com.android.internal.policy.ScreenDecorationsUtils;
+
/**
* Various shared constants between Launcher and SysUI as part of quickstep
*/
@@ -31,6 +33,7 @@
public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
public static final String KEY_EXTRA_INPUT_CHANNEL = "extra_input_channel";
+ public static final String KEY_EXTRA_INPUT_MONITOR = "extra_input_monitor";
public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners";
@@ -129,5 +132,19 @@
com.android.internal.R.dimen.config_backGestureInset);
}
+ /**
+ * Corner radius that should be used on windows in order to cover the display.
+ * These values are expressed in pixels because they should not respect display or font
+ * scaling, this means that we don't have to reload them on config changes.
+ */
+ public static float getWindowCornerRadius(Resources resources) {
+ return ScreenDecorationsUtils.getWindowCornerRadius(resources);
+ }
+ /**
+ * If live rounded corners are supported on windows.
+ */
+ public static boolean supportsRoundedCornersOnWindows(Resources resources) {
+ return ScreenDecorationsUtils.supportsRoundedCornersOnWindows(resources);
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index fd92e9e..b738b57 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -187,6 +187,7 @@
mBigClockContainer.removeAllViews();
updateBigClockVisibility();
}
+ mClockPlugin.onDestroyView();
mClockPlugin = null;
}
if (plugin == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index 147def39..d30f45f 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -102,6 +102,16 @@
}
@Override
+ public void onDestroyView() {
+ mView = null;
+ mDigitalClock = null;
+ mAnalogClock = null;
+ mLockClockContainer = null;
+ mLockClock = null;
+ mDarkController = null;
+ }
+
+ @Override
public String getName() {
return "bubble";
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
index 73414b3..488cb27 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
@@ -93,6 +93,13 @@
}
@Override
+ public void onDestroyView() {
+ mView = null;
+ mTextTime = null;
+ mTextDate = null;
+ }
+
+ @Override
public String getName() {
return "default";
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
index ea9f0cd..81b6a60 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
@@ -101,6 +101,17 @@
mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock);
}
+
+ @Override
+ public void onDestroyView() {
+ mBigClockView = null;
+ mDigitalClock = null;
+ mAnalogClock = null;
+ mView = null;
+ mLockClock = null;
+ mDarkController = null;
+ }
+
@Override
public String getName() {
return "stretch";
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
index 67c0989..1c6b38b 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
@@ -99,6 +99,14 @@
}
@Override
+ public void onDestroyView() {
+ mView = null;
+ mTypeClock = null;
+ mLockClock = null;
+ mDarkController = null;
+ }
+
+ @Override
public String getName() {
return "type";
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 3aa9f73..a5aed87 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -395,7 +395,6 @@
}
if (shouldAutoBubbleForFlags(mContext, entry) || shouldBubble(entry)) {
// TODO: handle group summaries
- entry.setIsBubble(true);
boolean suppressNotification = entry.getBubbleMetadata() != null
&& entry.getBubbleMetadata().getSuppressInitialNotification()
&& isForegroundApp(entry.notification.getPackageName());
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index be55829..de4605b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -42,7 +42,9 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
+import android.widget.TextView;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -70,6 +72,13 @@
private static final String TAG = "BubbleStackView";
private static final boolean DEBUG = false;
+ /** Duration of the flyout alpha animations. */
+ private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
+
+ /** How long to wait, in milliseconds, before hiding the flyout. */
+ @VisibleForTesting
+ static final int FLYOUT_HIDE_AFTER = 5000;
+
/**
* Interface to synchronize {@link View} state and the screen.
*
@@ -119,6 +128,14 @@
private FrameLayout mExpandedViewContainer;
+ private View mFlyout;
+ private TextView mFlyoutText;
+ /** Spring animation for the flyout. */
+ private SpringAnimation mFlyoutSpring;
+ /** Runnable that fades out the flyout and then sets it to GONE. */
+ private Runnable mHideFlyout =
+ () -> mFlyout.animate().alpha(0f).withEndAction(() -> mFlyout.setVisibility(GONE));
+
private int mBubbleSize;
private int mBubblePadding;
private int mExpandedAnimateXDistance;
@@ -131,6 +148,9 @@
private boolean mIsExpanded;
private boolean mImeVisible;
+ /** Whether the stack is currently being dragged. */
+ private boolean mIsDragging = false;
+
private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
private BubbleExpandedView.OnBubbleBlockedListener mBlockedListener;
@@ -221,6 +241,17 @@
mExpandedViewContainer.setClipChildren(false);
addView(mExpandedViewContainer);
+ mFlyout = mInflater.inflate(R.layout.bubble_flyout, this, false);
+ mFlyout.setVisibility(GONE);
+ mFlyout.animate()
+ .setDuration(FLYOUT_ALPHA_ANIMATION_DURATION)
+ .setInterpolator(new AccelerateDecelerateInterpolator());
+ addView(mFlyout);
+
+ mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
+
+ mFlyoutSpring = new SpringAnimation(mFlyout, DynamicAnimation.TRANSLATION_X);
+
mExpandedViewXAnim =
new SpringAnimation(mExpandedViewContainer, DynamicAnimation.TRANSLATION_X);
mExpandedViewXAnim.setSpring(
@@ -448,6 +479,8 @@
requestUpdate();
logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+
+ animateInFlyoutForBubble(b);
}
/**
@@ -549,6 +582,7 @@
mBubbleContainer.moveViewTo(b.iconView, 0);
}
requestUpdate();
+ animateInFlyoutForBubble(b /* bubble */);
}
if (mIsExpanded && entry.equals(mExpandedBubble.entry)) {
entry.setShowInShadeWhenBubble(false);
@@ -577,11 +611,18 @@
}
// Outside parts of view we care about.
return null;
+ } else if (isIntersecting(mFlyout, x, y)) {
+ return mFlyout;
}
- // If we're collapsed, the stack is always the target.
+
+ // If it wasn't an individual bubble in the expanded state, or the flyout, it's the stack.
return this;
}
+ public View getFlyoutView() {
+ return mFlyout;
+ }
+
/**
* Collapses the stack of bubbles.
* <p>
@@ -622,6 +663,8 @@
*/
private void animateExpansion(boolean shouldExpand) {
if (mIsExpanded != shouldExpand) {
+ hideFlyoutImmediate();
+
mIsExpanded = shouldExpand;
updateExpandedBubble();
applyCurrentState();
@@ -735,6 +778,9 @@
mStackAnimationController.cancelStackPositionAnimations();
mBubbleContainer.setController(mStackAnimationController);
+ hideFlyoutImmediate();
+
+ mIsDragging = true;
}
void onDragged(float x, float y) {
@@ -747,6 +793,7 @@
void onDragFinish(float x, float y, float velX, float velY) {
// TODO: Add fling to bottom to dismiss.
+ mIsDragging = false;
if (mIsExpanded || mIsExpansionAnimating) {
return;
@@ -797,6 +844,47 @@
}
}
+ /**
+ * Animates in the flyout for the given bubble, if available, and then hides it after some time.
+ */
+ @VisibleForTesting
+ void animateInFlyoutForBubble(Bubble bubble) {
+ final CharSequence updateMessage = bubble.entry.getUpdateMessage(getContext());
+
+ // Show the message if one exists, and we're not expanded or animating expansion.
+ if (updateMessage != null && !isExpanded() && !mIsExpansionAnimating && !mIsDragging) {
+ final PointF stackPos = mStackAnimationController.getStackPosition();
+
+ mFlyoutText.setText(updateMessage);
+ mFlyout.measure(WRAP_CONTENT, WRAP_CONTENT);
+ mFlyout.post(() -> {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ final float destinationX = onLeft
+ ? stackPos.x + mBubbleSize + mBubblePadding
+ : stackPos.x - mFlyout.getMeasuredWidth();
+
+ // Translate towards the stack slightly, then spring out from the stack.
+ mFlyout.setTranslationX(destinationX + (onLeft ? -mBubblePadding : mBubblePadding));
+ mFlyout.setTranslationY(stackPos.y);
+ mFlyout.setAlpha(0f);
+
+ mFlyout.setVisibility(VISIBLE);
+
+ mFlyout.animate().alpha(1f);
+ mFlyoutSpring.animateToFinalPosition(destinationX);
+
+ mFlyout.removeCallbacks(mHideFlyout);
+ mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
+ });
+ }
+ }
+
+ /** Hide the flyout immediately and cancel any pending hide runnables. */
+ private void hideFlyoutImmediate() {
+ mFlyout.removeCallbacks(mHideFlyout);
+ mHideFlyout.run();
+ }
+
@Override
public void getBoundsOnScreen(Rect outRect) {
if (!mIsExpanded) {
@@ -806,6 +894,12 @@
} else {
mBubbleContainer.getBoundsOnScreen(outRect);
}
+
+ if (mFlyout.getVisibility() == View.VISIBLE) {
+ final Rect flyoutBounds = new Rect();
+ mFlyout.getBoundsOnScreen(flyoutBounds);
+ outRect.union(flyoutBounds);
+ }
}
private int getStatusBarHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index a7170d0..baeedaa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -86,6 +86,7 @@
}
final boolean isStack = mStack.equals(mTouchedView);
+ final boolean isFlyout = mStack.getFlyoutView().equals(mTouchedView);
final float rawX = event.getRawX();
final float rawY = event.getRawY();
@@ -96,14 +97,18 @@
case MotionEvent.ACTION_DOWN:
trackMovement(event);
- mDismissViewController.createDismissTarget();
- mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY);
-
mTouchDown.set(rawX, rawY);
+ if (!isFlyout) {
+ mDismissViewController.createDismissTarget();
+ mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY);
+ }
+
if (isStack) {
mViewPositionOnTouchDown.set(mStack.getStackPosition());
mStack.onDragStart();
+ } else if (isFlyout) {
+ // TODO(b/129768381): Make the flyout dismissable with a gesture.
} else {
mViewPositionOnTouchDown.set(
mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
@@ -123,6 +128,8 @@
if (mMovedEnough) {
if (isStack) {
mStack.onDragged(viewX, viewY);
+ } else if (isFlyout) {
+ // TODO(b/129768381): Make the flyout dismissable with a gesture.
} else {
mStack.onBubbleDragged(mTouchedView, viewX, viewY);
}
@@ -141,6 +148,11 @@
trackMovement(event);
if (mInDismissTarget && isStack) {
mController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ } else if (isFlyout) {
+ // TODO(b/129768381): Expand if tapped, dismiss if swiped away.
+ if (!mStack.isExpanded() && !mMovedEnough) {
+ mStack.expandStack();
+ }
} else if (mMovedEnough) {
mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
final float velX = mVelocityTracker.getXVelocity();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 3b9164d..84b86bf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -27,7 +27,6 @@
import android.graphics.drawable.InsetDrawable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
-import android.widget.TextView;
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.Interpolators;
@@ -49,7 +48,6 @@
private Context mContext;
private BadgedImageView mBadgedImageView;
- private TextView mMessageView;
private int mPadding;
private int mIconInset;
@@ -78,10 +76,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mBadgedImageView = (BadgedImageView) findViewById(R.id.bubble_image);
- mMessageView = (TextView) findViewById(R.id.message_view);
- mMessageView.setVisibility(GONE);
- mMessageView.setPivotX(0);
+ mBadgedImageView = findViewById(R.id.bubble_image);
}
@Override
@@ -89,33 +84,6 @@
super.onAttachedToWindow();
}
- @Override
- protected void onMeasure(int widthSpec, int heightSpec) {
- measureChild(mBadgedImageView, widthSpec, heightSpec);
- measureChild(mMessageView, widthSpec, heightSpec);
- boolean messageGone = mMessageView.getVisibility() == GONE;
- int imageHeight = mBadgedImageView.getMeasuredHeight();
- int imageWidth = mBadgedImageView.getMeasuredWidth();
- int messageHeight = messageGone ? 0 : mMessageView.getMeasuredHeight();
- int messageWidth = messageGone ? 0 : mMessageView.getMeasuredWidth();
- setMeasuredDimension(
- getPaddingStart() + imageWidth + mPadding + messageWidth + getPaddingEnd(),
- getPaddingTop() + Math.max(imageHeight, messageHeight) + getPaddingBottom());
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- left = getPaddingStart();
- top = getPaddingTop();
- int imageWidth = mBadgedImageView.getMeasuredWidth();
- int imageHeight = mBadgedImageView.getMeasuredHeight();
- int messageWidth = mMessageView.getMeasuredWidth();
- int messageHeight = mMessageView.getMeasuredHeight();
- mBadgedImageView.layout(left, top, left + imageWidth, top + imageHeight);
- mMessageView.layout(left + imageWidth + mPadding, top,
- left + imageWidth + mPadding + messageWidth, top + messageHeight);
- }
-
/**
* Populates this view with a notification.
* <p>
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 c395031..78c4fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -157,6 +157,15 @@
return mStackPosition;
}
+ /** Whether the stack is on the left side of the screen. */
+ public boolean isStackOnLeftSide() {
+ if (mLayout != null) {
+ return mStackPosition.x - mIndividualBubbleSize / 2 < mLayout.getWidth() / 2;
+ } else {
+ return false;
+ }
+ }
+
/**
* Flings the stack starting with the given velocities, springing it to the nearest edge
* afterward.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 3140e6d..3ec6cb7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -795,7 +795,10 @@
? mExpandedMovementBounds
: mNormalMovementBounds;
try {
- mPinnedStackController.setMinEdgeSize(isMenuExpanded ? mExpandedShortestEdgeSize : 0);
+ if (mPinnedStackController != null) {
+ mPinnedStackController.setMinEdgeSize(
+ isMenuExpanded ? mExpandedShortestEdgeSize : 0);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Could not set minimized state", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 10eacba..644664e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.Intent;
-import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.PorterDuff.Mode;
@@ -320,29 +319,13 @@
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
- mMultiUserSwitch.setVisibility(showUserSwitcher(isDemo) ? View.VISIBLE : View.INVISIBLE);
+ mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE);
mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
}
- private boolean showUserSwitcher(boolean isDemo) {
- if (!mExpanded || isDemo || !UserManager.supportsMultipleUsers()) {
- return false;
- }
- UserManager userManager = UserManager.get(mContext);
- if (userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
- return false;
- }
- int switchableUserCount = 0;
- for (UserInfo user : userManager.getUsers(true)) {
- if (user.supportsSwitchToByUser()) {
- ++switchableUserCount;
- if (switchableUserCount > 1) {
- return true;
- }
- }
- }
- return getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user);
+ private boolean showUserSwitcher() {
+ return mExpanded && mMultiUserSwitch.isMultiUserEnabled();
}
private void updateListeners() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 415870c..b1dfbb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -21,7 +21,7 @@
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
-import android.media.projection.MediaProjectionInfo;
+import android.media.MediaRouter.RouteInfo;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.Log;
@@ -48,8 +48,9 @@
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.NetworkController;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
-import java.util.Set;
+import java.util.List;
import javax.inject.Inject;
@@ -128,35 +129,30 @@
return;
}
- CastDevice activeProjection = getActiveDeviceMediaProjection();
- if (activeProjection == null) {
- if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) {
- mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
- showDetail(true);
- });
- } else {
+ List<CastDevice> activeDevices = getActiveDevices();
+ // We want to pop up the media route selection dialog if we either have no active devices
+ // (neither routes nor projection), or if we have an active route. In other cases, we assume
+ // that a projection is active. This is messy, but this tile never correctly handled the
+ // case where multiple devices were active :-/.
+ if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) {
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
showDetail(true);
- }
+ });
} else {
- mController.stopCasting(activeProjection);
+ mController.stopCasting(activeDevices.get(0));
}
}
- private CastDevice getActiveDeviceMediaProjection() {
- CastDevice activeDevice = null;
+ private List<CastDevice> getActiveDevices() {
+ ArrayList<CastDevice> activeDevices = new ArrayList<>();
for (CastDevice device : mController.getCastDevices()) {
if (device.state == CastDevice.STATE_CONNECTED
|| device.state == CastDevice.STATE_CONNECTING) {
- activeDevice = device;
- break;
+ activeDevices.add(device);
}
}
- if (activeDevice != null && activeDevice.tag instanceof MediaProjectionInfo) {
- return activeDevice;
- }
-
- return null;
+ return activeDevices;
}
@Override
@@ -187,14 +183,18 @@
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.contentDescription = state.label;
state.value = false;
- final Set<CastDevice> devices = mController.getCastDevices();
+ final List<CastDevice> devices = mController.getCastDevices();
boolean connecting = false;
+ // We always choose the first device that's in the CONNECTED state in the case where
+ // multiple devices are CONNECTED at the same time.
for (CastDevice device : devices) {
if (device.state == CastDevice.STATE_CONNECTED) {
state.value = true;
state.secondaryLabel = getDeviceName(device);
state.contentDescription = state.contentDescription + "," +
mContext.getString(R.string.accessibility_cast_name, state.label);
+ connecting = false;
+ break;
} else if (device.state == CastDevice.STATE_CONNECTING) {
connecting = true;
}
@@ -326,7 +326,7 @@
return mItems;
}
- private void updateItems(Set<CastDevice> devices) {
+ private void updateItems(List<CastDevice> devices) {
if (mItems == null) return;
Item[] items = null;
if (devices != null && !devices.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ead39c69..2b361f8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -24,7 +24,7 @@
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_CHANNEL;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
@@ -38,6 +38,7 @@
import android.content.ServiceConnection;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -47,7 +48,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import android.view.InputChannel;
+import android.view.InputMonitor;
import android.view.MotionEvent;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -58,7 +59,6 @@
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -115,8 +115,6 @@
private float mWindowCornerRadius;
private boolean mSupportsRoundedCornersOnWindows;
- private InputEventDispatcher mInputEventDispatcher;
-
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
public void startScreenPinning(int taskId) {
@@ -295,6 +293,22 @@
}
}
+ public Bundle monitorGestureInput(String name, int displayId) {
+ if (!verifyCaller("monitorGestureInput")) {
+ return null;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ InputMonitor monitor =
+ InputManager.getInstance().monitorGestureInput(name, displayId);
+ Bundle result = new Bundle();
+ result.putParcelable(KEY_EXTRA_INPUT_MONITOR, monitor);
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -341,23 +355,16 @@
} catch (RemoteException e) {
Log.e(TAG_OPS, "Lost connection to launcher service", e);
}
- try {
- mOverviewProxy.onBind(mSysUiProxy);
- } catch (RemoteException e) {
- mCurrentBoundedUserId = -1;
- Log.e(TAG_OPS, "Failed to call onBind()", e);
- }
Bundle params = new Bundle();
params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
- params.putParcelable(KEY_EXTRA_INPUT_CHANNEL, createNewInputDispatcher());
params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
try {
mOverviewProxy.onInitialize(params);
} catch (RemoteException e) {
- // Ignore error until the migration is complete.
- Log.e(TAG_OPS, "Failed to call onBind()", e);
+ mCurrentBoundedUserId = -1;
+ Log.e(TAG_OPS, "Failed to call onInitialize()", e);
}
dispatchNavButtonBounds();
@@ -369,7 +376,6 @@
Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
mCurrentBoundedUserId = -1;
retryConnectionWithBackoff();
- disposeInputDispatcher();
}
@Override
@@ -377,32 +383,15 @@
Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
mCurrentBoundedUserId = -1;
retryConnectionWithBackoff();
- disposeInputDispatcher();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// Do nothing
mCurrentBoundedUserId = -1;
- disposeInputDispatcher();
}
};
- private void disposeInputDispatcher() {
- if (mInputEventDispatcher != null) {
- mInputEventDispatcher.dispose();
- mInputEventDispatcher = null;
- }
- }
-
- private InputChannel createNewInputDispatcher() {
- disposeInputDispatcher();
-
- InputChannel[] channels = InputChannel.openInputChannelPair("overview-proxy-service");
- mInputEventDispatcher = new InputEventDispatcher(channels[0], Looper.getMainLooper());
- return channels[1];
- }
-
private final DeviceProvisionedListener mDeviceProvisionedCallback =
new DeviceProvisionedListener() {
@Override
@@ -567,10 +556,6 @@
return mOverviewProxy;
}
- public InputEventDispatcher getInputEventDispatcher() {
- return mInputEventDispatcher;
- }
-
public int getInteractionFlags() {
return mInteractionFlags;
}
@@ -633,6 +618,14 @@
}
}
+ public void notifyAssistantVisibilityChanged(float visibility) {
+ try {
+ mOverviewProxy.onAssistantVisibilityChanged(visibility);
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onAssistantVisibilityChanged()", e);
+ }
+ }
+
private void updateEnabledState() {
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
MATCH_SYSTEM_ONLY,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index a630e49..2793b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -63,6 +63,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.Set;
import javax.inject.Inject;
@@ -142,7 +143,7 @@
if (DEBUG) {
Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
}
- logActionClick(view);
+ logActionClick(view, pendingIntent);
// The intent we are sending is for the application, which
// won't have permission to immediately start an activity after
// the user switches to home. We know it is safe to do at this
@@ -159,11 +160,11 @@
});
}
- private void logActionClick(View view) {
+ private void logActionClick(View view, PendingIntent actionIntent) {
Integer actionIndex = (Integer)
view.getTag(com.android.internal.R.id.notification_action_index_tag);
if (actionIndex == null) {
- Log.e(TAG, "Couldn't retrieve the actionIndex from the clicked button");
+ // Custom action button, not logging.
return;
}
ViewParent parent = view.getParent();
@@ -182,8 +183,20 @@
}
final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
final int rank = mEntryManager.getNotificationData().getRank(key);
+
+ // Notification may be updated before this function is executed, and thus play safe
+ // here and verify that the action object is still the one that where the click happens.
+ Notification.Action[] actions = statusBarNotification.getNotification().actions;
+ if (actions == null || actionIndex >= actions.length) {
+ Log.w(TAG, "statusBarNotification.getNotification().actions is null or invalid");
+ return;
+ }
final Notification.Action action =
statusBarNotification.getNotification().actions[actionIndex];
+ if (Objects.equals(action.actionIntent, actionIntent)) {
+ Log.w(TAG, "actionIntent does not match");
+ return;
+ }
NotificationVisibility.NotificationLocation location =
NotificationLogger.getNotificationLocation(
mEntryManager.getNotificationData().get(key));
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 f1373d1..f69356e 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
@@ -21,6 +21,7 @@
import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
@@ -40,6 +41,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.view.View;
import android.widget.ImageView;
@@ -50,6 +52,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.R;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.InflationException;
@@ -146,11 +149,6 @@
private boolean hasSentReply;
/**
- * Whether this notification should be displayed as a bubble.
- */
- private boolean mIsBubble;
-
- /**
* Whether this notification has been approved globally, at the app level, and at the channel
* level for bubbling.
*/
@@ -222,12 +220,8 @@
this.mHighPriority = highPriority;
}
- public void setIsBubble(boolean bubbleable) {
- mIsBubble = bubbleable;
- }
-
public boolean isBubble() {
- return mIsBubble;
+ return (notification.getNotification().flags & FLAG_BUBBLE) != 0;
}
public void setBubbleDismissed(boolean userDismissed) {
@@ -401,6 +395,72 @@
}
/**
+ * Returns our best guess for the most relevant text summary of the latest update to this
+ * notification, based on its type. Returns null if there should not be an update message.
+ */
+ public CharSequence getUpdateMessage(Context context) {
+ final Notification underlyingNotif = notification.getNotification();
+ final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
+
+ try {
+ if (Notification.BigTextStyle.class.equals(style)) {
+ // Return the big text, it is big so probably important. If it's not there use the
+ // normal text.
+ CharSequence bigText =
+ underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
+ return !TextUtils.isEmpty(bigText)
+ ? bigText
+ : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+ } else if (Notification.MessagingStyle.class.equals(style)) {
+ final List<Notification.MessagingStyle.Message> messages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+ (Parcelable[]) underlyingNotif.extras.get(
+ Notification.EXTRA_MESSAGES));
+
+ final Notification.MessagingStyle.Message latestMessage =
+ Notification.MessagingStyle.findLatestIncomingMessage(messages);
+
+ if (latestMessage != null) {
+ final CharSequence personName = latestMessage.getSenderPerson() != null
+ ? latestMessage.getSenderPerson().getName()
+ : null;
+
+ // Prepend the sender name if available since group chats also use messaging
+ // style.
+ if (!TextUtils.isEmpty(personName)) {
+ return context.getResources().getString(
+ R.string.notification_summary_message_format,
+ personName,
+ latestMessage.getText());
+ } else {
+ return latestMessage.getText();
+ }
+ }
+ } else if (Notification.InboxStyle.class.equals(style)) {
+ CharSequence[] lines =
+ underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
+
+ // Return the last line since it should be the most recent.
+ if (lines != null && lines.length > 0) {
+ return lines[lines.length - 1];
+ }
+ } else if (Notification.MediaStyle.class.equals(style)) {
+ // Return nothing, media updates aren't typically useful as a text update.
+ return null;
+ } else {
+ // Default to text extra.
+ return underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+ }
+ } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
+ // No use crashing, we'll just return null and the caller will assume there's no update
+ // message.
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
* Abort all existing inflation tasks
*/
public void abortTask() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index ca45209..2e85fea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -414,7 +414,9 @@
return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
}
if (mStatusBarKeyguardViewManager.isShowing()) {
- if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
+ if ((mStatusBarKeyguardViewManager.isBouncerShowing()
+ || mStatusBarKeyguardViewManager.isBouncerPartiallyVisible())
+ && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed) {
return faceStayingOnKeyguard ? MODE_ONLY_WAKE : MODE_UNLOCK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
new file mode 100644
index 0000000..95abb66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -0,0 +1,351 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.hardware.input.InputManager;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.Choreographer;
+import android.view.Gravity;
+import android.view.IPinnedStackController;
+import android.view.IPinnedStackListener;
+import android.view.ISystemGestureExclusionListener;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputMonitor;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.systemui.R;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Utility class to handle edge swipes for back gesture
+ */
+public class EdgeBackGestureHandler implements DisplayListener {
+
+ private static final String TAG = "EdgeBackGestureHandler";
+
+ private final IPinnedStackListener.Stub mImeChangedListener = new IPinnedStackListener.Stub() {
+ @Override
+ public void onListenerRegistered(IPinnedStackController controller) {
+ }
+
+ @Override
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ // No need to thread jump, assignments are atomic
+ mImeHeight = imeVisible ? imeHeight : 0;
+ // TODO: Probably cancel any existing gesture
+ }
+
+ @Override
+ public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
+ }
+
+ @Override
+ public void onMinimizedStateChanged(boolean isMinimized) {
+ }
+
+ @Override
+ public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
+ Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
+ int displayRotation) {
+ }
+
+ @Override
+ public void onActionsChanged(ParceledListSlice actions) {
+ }
+ };
+
+ private ISystemGestureExclusionListener mGestureExclusionListener =
+ new ISystemGestureExclusionListener.Stub() {
+ @Override
+ public void onSystemGestureExclusionChanged(int displayId,
+ Region systemGestureExclusion) {
+ if (displayId == mDisplayId) {
+ mMainExecutor.execute(() -> mExcludeRegion.set(systemGestureExclusion));
+ }
+ }
+ };
+
+ private final Context mContext;
+
+ private final Point mDisplaySize = new Point();
+ private final int mDisplayId;
+
+ private final Executor mMainExecutor;
+
+ private final Region mExcludeRegion = new Region();
+ // The edge width where touch down is allowed
+ private final int mEdgeWidth;
+ // The slop to distinguish between horizontal and vertical motion
+ private final float mTouchSlop;
+ // Minimum distance to move so that is can be considerd as a back swipe
+ private final float mSwipeThreshold;
+
+ private final int mNavBarHeight;
+
+ private final PointF mDownPoint = new PointF();
+ private boolean mThresholdCrossed = false;
+ private boolean mIgnoreThisGesture = false;
+ private boolean mIsOnLeftEdge;
+
+ private int mImeHeight = 0;
+
+ private boolean mIsAttached;
+ private boolean mIsGesturalModeEnabled;
+ private boolean mIsEnabled;
+
+ private InputMonitor mInputMonitor;
+ private InputEventReceiver mInputEventReceiver;
+
+ private final WindowManager mWm;
+
+ private NavigationBarEdgePanel mEdgePanel;
+ private WindowManager.LayoutParams mEdgePanelLp;
+
+ public EdgeBackGestureHandler(Context context) {
+ mContext = context;
+ mDisplayId = context.getDisplayId();
+ mMainExecutor = context.getMainExecutor();
+ mWm = context.getSystemService(WindowManager.class);
+
+ mEdgeWidth = QuickStepContract.getEdgeSensitivityWidth(context);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mSwipeThreshold = context.getResources()
+ .getDimension(R.dimen.navigation_edge_action_drag_threshold);
+
+ mNavBarHeight = context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
+ }
+
+ /**
+ * @see NavigationBarView#onAttachedToWindow()
+ */
+ public void onNavBarAttached() {
+ mIsAttached = true;
+ onOverlaysChanged();
+ }
+
+ /**
+ * @see NavigationBarView#onDetachedFromWindow()
+ */
+ public void onNavBarDetached() {
+ mIsAttached = false;
+ updateIsEnabled();
+ }
+
+ /**
+ * Called when system overlays has changed
+ */
+ public void onOverlaysChanged() {
+ mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mContext);
+ updateIsEnabled();
+ }
+
+ private void disposeInputChannel() {
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ }
+ if (mInputMonitor != null) {
+ mInputMonitor.dispose();
+ mInputMonitor = null;
+ }
+ }
+
+ private void updateIsEnabled() {
+ boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
+ if (isEnabled == mIsEnabled) {
+ return;
+ }
+ mIsEnabled = isEnabled;
+ disposeInputChannel();
+
+ if (mEdgePanel != null) {
+ mWm.removeView(mEdgePanel);
+ mEdgePanel = null;
+ }
+
+ if (!mIsEnabled) {
+ WindowManagerWrapper.getInstance().removePinnedStackListener(mImeChangedListener);
+
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .unregisterSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister window manager callbacks", e);
+ }
+
+ } else {
+ updateDisplaySize();
+
+ try {
+ WindowManagerWrapper.getInstance().addPinnedStackListener(mImeChangedListener);
+ WindowManagerGlobal.getWindowManagerService()
+ .registerSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register window manager callbacks", e);
+ }
+
+ // Register input event receiver
+ mInputMonitor = InputManager.getInstance().monitorGestureInput(
+ "edge-swipe", mDisplayId);
+ mInputEventReceiver = new InputEventReceiver(mInputMonitor.getInputChannel(),
+ Looper.getMainLooper(), Choreographer.getMainThreadInstance(),
+ this::onInputEvent);
+
+ // Add a nav bar panel window
+ mEdgePanel = new NavigationBarEdgePanel(mContext);
+ mEdgePanelLp = new WindowManager.LayoutParams(
+ mContext.getResources()
+ .getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
+ mContext.getResources()
+ .getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ PixelFormat.TRANSLUCENT);
+ mEdgePanelLp.setTitle(TAG + mDisplayId);
+ mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
+ mEdgePanelLp.windowAnimations = 0;
+ mEdgePanel.setLayoutParams(mEdgePanelLp);
+ mWm.addView(mEdgePanel, mEdgePanelLp);
+ }
+ }
+
+ private void onInputEvent(InputEvent ev) {
+ if (ev instanceof MotionEvent) {
+ onMotionEvent((MotionEvent) ev);
+ }
+ }
+
+ private boolean isWithinTouchRegion(int x, int y) {
+ if (y > (mDisplaySize.y - Math.max(mImeHeight, mNavBarHeight))) {
+ return false;
+ }
+
+ if (x > mEdgeWidth && x < (mDisplaySize.x - mEdgeWidth)) {
+ return false;
+ }
+ return !mExcludeRegion.contains(x, y);
+ }
+
+ private void onMotionEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ // Verify if this is in within the touch region
+ mIgnoreThisGesture = !isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
+ if (!mIgnoreThisGesture) {
+ mIsOnLeftEdge = ev.getX() < mEdgeWidth;
+ mEdgePanelLp.gravity = mIsOnLeftEdge
+ ? (Gravity.LEFT | Gravity.TOP)
+ : (Gravity.RIGHT | Gravity.TOP);
+ mEdgePanel.setIsLeftPanel(mIsOnLeftEdge);
+ mEdgePanelLp.y = MathUtils.constrain(
+ (int) (ev.getY() - mEdgePanelLp.height / 2),
+ 0, mDisplaySize.y);
+ mWm.updateViewLayout(mEdgePanel, mEdgePanelLp);
+
+ mDownPoint.set(ev.getX(), ev.getY());
+ mThresholdCrossed = false;
+ mEdgePanel.handleTouch(ev);
+ }
+ } else if (!mIgnoreThisGesture) {
+ if (!mThresholdCrossed && ev.getAction() == MotionEvent.ACTION_MOVE) {
+ float dx = Math.abs(ev.getX() - mDownPoint.x);
+ float dy = Math.abs(ev.getY() - mDownPoint.y);
+ if (dy > dx && dy > mTouchSlop) {
+ // Send action cancel to reset all the touch events
+ mIgnoreThisGesture = true;
+ MotionEvent cancelEv = MotionEvent.obtain(ev);
+ cancelEv.setAction(MotionEvent.ACTION_CANCEL);
+ mEdgePanel.handleTouch(cancelEv);
+ cancelEv.recycle();
+ return;
+
+ } else if (dx > dy && dx > mTouchSlop) {
+ mThresholdCrossed = true;
+ // Capture inputs
+ mInputMonitor.pilferPointers();
+ }
+ }
+
+ // forward touch
+ mEdgePanel.handleTouch(ev);
+
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ float xDiff = ev.getX() - mDownPoint.x;
+ boolean exceedsThreshold = mIsOnLeftEdge
+ ? (xDiff > mSwipeThreshold) : (-xDiff > mSwipeThreshold);
+ if (exceedsThreshold && Math.abs(xDiff) > Math.abs(ev.getY() - mDownPoint.y)) {
+ // Perform back
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) { }
+
+ @Override
+ public void onDisplayRemoved(int displayId) { }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDisplayId) {
+ updateDisplaySize();
+ }
+ }
+
+ private void updateDisplaySize() {
+ mContext.getSystemService(DisplayManager.class)
+ .getDisplay(mDisplayId).getRealSize(mDisplaySize);
+ }
+
+ private void sendEvent(int action, int code) {
+ long when = SystemClock.uptimeMillis();
+ final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
+ 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+ KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+ InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 5afff81..1d2ca9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -334,6 +334,11 @@
&& mExpansion == EXPANSION_VISIBLE && !isAnimatingAway();
}
+ public boolean isPartiallyVisible() {
+ return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE))
+ && mExpansion != EXPANSION_HIDDEN && !isAnimatingAway();
+ }
+
/**
* @return {@code true} when bouncer's pre-hide animation already started but isn't completely
* hidden yet, {@code false} otherwise.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 2bd8d41..6ee031a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -187,9 +187,9 @@
}
if (mKeyguardUserSwitcher == null) {
// If we have no keyguard switcher, the screen width is under 600dp. In this case,
- // we don't show the multi-user avatar unless there is more than 1 user on the device.
- if (mUserSwitcherController != null
- && mUserSwitcherController.getSwitchableUserCount() > 1) {
+ // we only show the multi-user switch if it's enabled through UserManager as well as
+ // by the user.
+ if (mMultiUserSwitch.isMultiUserEnabled()) {
mMultiUserSwitch.setVisibility(View.VISIBLE);
} else {
mMultiUserSwitch.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index f393dcd..1d87a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.phone;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.Intent;
import android.os.UserManager;
-import android.provider.ContactsContract;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
@@ -33,7 +33,6 @@
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -95,6 +94,26 @@
registerListener();
}
+ public boolean isMultiUserEnabled() {
+ // Short-circuiting from UserManager. Needs to be extracted because of SystemUI boolean flag
+ // qs_show_user_switcher_for_single_user
+
+ final boolean userSwitcherEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.USER_SWITCHER_ENABLED, 1) != 0;
+
+ if (!UserManager.supportsMultipleUsers()
+ || mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)
+ || UserManager.isDeviceInDemoMode(mContext)
+ || !userSwitcherEnabled) {
+ return false;
+ }
+
+ final boolean guestEnabled = !mContext.getSystemService(DevicePolicyManager.class)
+ .getGuestUserDisabled(null);
+ return mUserSwitcherController.getSwitchableUserCount() > 1 || guestEnabled
+ || mContext.getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user);
+ }
+
private void registerListener() {
if (mUserManager.isUserSwitcherEnabled() && mUserListener == null) {
@@ -118,29 +137,20 @@
@Override
public void onClick(View v) {
- if (mUserManager.isUserSwitcherEnabled()) {
- if (mKeyguardMode) {
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.show(true /* animate */);
- }
- } else if (mQsPanel != null && mUserSwitcherController != null) {
- View center = getChildCount() > 0 ? getChildAt(0) : this;
-
- center.getLocationInWindow(mTmpInt2);
- mTmpInt2[0] += center.getWidth() / 2;
- mTmpInt2[1] += center.getHeight() / 2;
-
- mQsPanel.showDetailAdapter(true,
- getUserDetailAdapter(),
- mTmpInt2);
+ if (mKeyguardMode) {
+ if (mKeyguardUserSwitcher != null) {
+ mKeyguardUserSwitcher.show(true /* animate */);
}
- } else {
- if (mQsPanel != null) {
- Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
- getContext(), v, ContactsContract.Profile.CONTENT_URI,
- ContactsContract.QuickContact.MODE_LARGE, null);
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
- }
+ } else if (mQsPanel != null && mUserSwitcherController != null) {
+ View center = getChildCount() > 0 ? getChildAt(0) : this;
+
+ center.getLocationInWindow(mTmpInt2);
+ mTmpInt2[0] += center.getWidth() / 2;
+ mTmpInt2[1] += center.getHeight() / 2;
+
+ mQsPanel.showDetailAdapter(true,
+ getUserDetailAdapter(),
+ mTmpInt2);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java
deleted file mode 100644
index 323e776..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationAssistantAction.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.annotation.NonNull;
-import android.os.Bundle;
-import android.view.MotionEvent;
-
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.recents.OverviewProxyService;
-
-/**
- * Assistant is triggered with this action
- */
-public class NavigationAssistantAction extends NavigationGestureAction {
- private static final String TAG = "NavigationAssistantActions";
-
- private final AssistManager mAssistManager;
-
- public NavigationAssistantAction(@NonNull NavigationBarView navigationBarView,
- @NonNull OverviewProxyService service, AssistManager assistManager) {
- super(navigationBarView, service);
- mAssistManager = assistManager;
- }
-
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- @Override
- public boolean disableProxyEvents() {
- return true;
- }
-
- @Override
- public void onGestureStart(MotionEvent event) {
- mAssistManager.startAssist(new Bundle());
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
deleted file mode 100644
index c77b16b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.annotation.NonNull;
-import android.hardware.input.InputManager;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-import com.android.systemui.recents.OverviewProxyService;
-
-/**
- * A back action when triggered will execute a back command
- */
-public class NavigationBackAction extends NavigationGestureAction {
-
- private static final String BACK_AFTER_END_PROP =
- "quickstepcontroller_homegoesbackwhenend";
- private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
- private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
-
- private final Handler mHandler = new Handler();
-
- private final Runnable mExecuteBackRunnable = new Runnable() {
- @Override
- public void run() {
- if (isEnabled() && canPerformAction()) {
- performBack();
- mHandler.postDelayed(this, BACK_GESTURE_POLL_TIMEOUT);
- }
- }
- };
-
- public NavigationBackAction(@NonNull NavigationBarView navigationBarView,
- @NonNull OverviewProxyService service) {
- super(navigationBarView, service);
- }
-
- @Override
- public boolean allowHitTargetToMoveOverDrag() {
- return true;
- }
-
- @Override
- public boolean canPerformAction() {
- return mProxySender.getBackButtonAlpha() > 0;
- }
-
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- @Override
- protected void onGestureStart(MotionEvent event) {
- if (!QuickStepController.shouldHideBackButton(getContext())) {
- mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
- BACK_BUTTON_FADE_OUT_ALPHA);
- }
- mHandler.removeCallbacks(mExecuteBackRunnable);
- if (!shouldExecuteBackOnUp()) {
- performBack();
- mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
- }
- }
-
- @Override
- protected void onGestureEnd() {
- mHandler.removeCallbacks(mExecuteBackRunnable);
- if (!QuickStepController.shouldHideBackButton(getContext())) {
- mNavigationBarView.getBackButton().setAlpha(
- mProxySender.getBackButtonAlpha(), true /* animate */);
- }
- if (shouldExecuteBackOnUp()) {
- performBack();
- }
- }
-
- @Override
- public boolean disableProxyEvents() {
- return true;
- }
-
- private void performBack() {
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- }
-
- private boolean shouldExecuteBackOnUp() {
- return getGlobalBoolean(BACK_AFTER_END_PROP);
- }
-
- private void sendEvent(int action, int code) {
- long when = SystemClock.uptimeMillis();
- final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
- 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
- KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
- InputDevice.SOURCE_KEYBOARD);
- InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
- }
-}
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 4897464..86b5344 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -17,25 +17,22 @@
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.os.SystemClock;
+import android.os.VibrationEffect;
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;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.VibratorHelper;
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;
@@ -52,6 +49,8 @@
private static final int ANIM_DURATION_MS = 150;
private static final long HAPTIC_TIMEOUT_MS = 200;
+ private final VibratorHelper mVibratorHelper;
+
private final Paint mPaint = new Paint();
private final Paint mProtectionPaint = new Paint();
@@ -63,9 +62,11 @@
private final float mPointExtent;
private final float mHeight;
private final float mStrokeThickness;
- private final boolean mIsLeftPanel;
- private float mStartY;
+ private final float mSwipeThreshold;
+
+ private boolean mIsLeftPanel;
+
private float mStartX;
private boolean mDragSlopPassed;
@@ -105,28 +106,11 @@
}
};
- public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
- int gravity) {
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- lp.gravity = gravity;
- lp.setTitle(TAG + context.getDisplayId());
- lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel);
- lp.windowAnimations = 0;
- NavigationBarEdgePanel panel = new NavigationBarEdgePanel(
- context, (gravity & Gravity.LEFT) == Gravity.LEFT);
- panel.setLayoutParams(lp);
- return panel;
- }
-
- private NavigationBarEdgePanel(Context context, boolean isLeftPanel) {
+ public NavigationBarEdgePanel(Context context) {
super(context);
+ mVibratorHelper = Dependency.get(VibratorHelper.class);
+
mEndAnimator = ObjectAnimator.ofFloat(this, DRAG_PROGRESS, 1f);
mEndAnimator.setAutoCancel(true);
mEndAnimator.setDuration(ANIM_DURATION_MS);
@@ -154,45 +138,16 @@
// Both panels arrow point the same way
mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
+
+ mSwipeThreshold = context.getResources()
+ .getDimension(R.dimen.navigation_edge_action_drag_threshold);
+ setVisibility(GONE);
+ }
+
+ public void setIsLeftPanel(boolean isLeftPanel) {
mIsLeftPanel = isLeftPanel;
}
- public void setWindowFlag(int flags, boolean enable) {
- WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
- if (lp == null || enable == ((lp.flags & flags) != 0)) {
- return;
- }
- if (enable) {
- lp.flags |= flags;
- } else {
- lp.flags &= ~flags;
- }
- updateLayout(lp);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN : {
- mDragSlopPassed = false;
- 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;
@@ -200,7 +155,7 @@
canvas.save();
canvas.translate(
mIsLeftPanel ? edgeOffset : getWidth() - edgeOffset,
- mStartY - mHeight * 0.5f);
+ (getHeight() - mHeight) * 0.5f);
float outsideX = mArrowsPointLeft ? animatedOffset : 0;
float middleX = mArrowsPointLeft ? 0 : animatedOffset;
@@ -223,15 +178,6 @@
mGestureLength = getWidth();
}
- public void setDimensions(int width, int height) {
- final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
- if (lp.width != width || lp.height != height) {
- lp.width = width;
- lp.height = height;
- updateLayout(lp);
- }
- }
-
private void setLegProgress(float progress) {
mLegProgress = progress;
invalidate();
@@ -251,29 +197,48 @@
}
private void hide() {
- animate().alpha(0f).setDuration(ANIM_DURATION_MS);
+ animate().alpha(0f).setDuration(ANIM_DURATION_MS)
+ .withEndAction(() -> setVisibility(GONE));
}
- 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;
+ /**
+ * Updates the UI based on the motion events passed in device co-ordinates
+ */
+ public void handleTouch(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN : {
+ mDragSlopPassed = false;
+ mEndAnimator.cancel();
+ mLegAnimator.cancel();
+ animate().cancel();
+ setLegProgress(0f);
+ setDragProgress(0f);
+ mStartX = event.getX();
+ setVisibility(VISIBLE);
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ handleNewSwipePoint(event.getX());
+ break;
+ }
+ // Fall through
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ hide();
+ break;
+ }
+ }
}
private void handleNewSwipePoint(float x) {
float dist = MathUtils.abs(x - mStartX);
// Apply a haptic on drag slop passed
- if (!mDragSlopPassed && dist > QuickStepContract.getQuickStepDragSlopPx()) {
+ if (!mDragSlopPassed && dist > mSwipeThreshold) {
mDragSlopPassed = true;
- performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
mLastSlopHapticTime = SystemClock.uptimeMillis();
+ setAlpha(1f);
}
setDragProgress(MathUtils.constrainedMap(
@@ -311,11 +276,6 @@
}
}
- 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/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 9485623..3dcadf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -91,6 +91,7 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -201,7 +202,7 @@
@Override
public void onBackButtonAlphaChanged(float alpha, boolean animate) {
final ButtonDispatcher backButton = mNavigationBarView.getBackButton();
- if (QuickStepController.shouldHideBackButton(getContext())) {
+ if (QuickStepContract.isGesturalMode(getContext())) {
// If property was changed to hide/show back button, going home will trigger
// launcher to to change the back button alpha to reflect property change
backButton.setVisibility(View.GONE);
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 f22ecf6..4fd6a71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,19 +17,9 @@
package com.android.systemui.statusbar.phone;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS;
@@ -40,14 +30,11 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.DrawableRes;
-import android.annotation.IntDef;
-import android.annotation.SuppressLint;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
@@ -56,15 +43,11 @@
import android.graphics.Region.Op;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
-import android.view.Gravity;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
@@ -92,31 +75,19 @@
import com.android.systemui.recents.RecentsOnboarding;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction;
-import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.function.Consumer;
public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> {
final static boolean DEBUG = false;
final static String TAG = "StatusBar/NavBarView";
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({WINDOW_TARGET_BOTTOM, WINDOW_TARGET_LEFT, WINDOW_TARGET_RIGHT})
- public @interface WindowTarget{}
- public static final int WINDOW_TARGET_BOTTOM = 0;
- public static final int WINDOW_TARGET_LEFT = 1;
- public static final int WINDOW_TARGET_RIGHT = 2;
-
// slippery nav bar when everything is disabled, e.g. during setup
final static boolean SLIPPERY_WHEN_DISABLED = true;
@@ -134,8 +105,6 @@
int mDisabledFlags = 0;
int mNavigationIconHints = 0;
- private @NavigationBarCompat.HitTarget int mDownHitTarget = HIT_TARGET_NONE;
- private @WindowTarget int mWindowHitTarget = WINDOW_TARGET_BOTTOM;
private Rect mHomeButtonBounds = new Rect();
private Rect mBackButtonBounds = new Rect();
private Rect mRecentsButtonBounds = new Rect();
@@ -148,6 +117,7 @@
private KeyButtonDrawable mRecentIcon;
private KeyButtonDrawable mDockedIcon;
+ private final EdgeBackGestureHandler mEdgeBackGestureHandler;
private GestureHelper mGestureHelper;
private final DeadZone mDeadZone;
private boolean mDeadZoneConsuming = false;
@@ -175,16 +145,6 @@
private NavBarTintController mTintController;
private boolean mAssistantAvailable;
- private NavigationPrototypeController mPrototypeController;
- private NavigationGestureAction[] mDefaultGestureMap;
- private QuickScrubAction mQuickScrubAction;
- private QuickStepAction mQuickStepAction;
- private NavigationBackAction mBackAction;
- private QuickSwitchAction mQuickSwitchAction;
- private NavigationAssistantAction mAssistantAction;
-
- private NavigationBarEdgePanel mLeftEdgePanel;
- private NavigationBarEdgePanel mRightEdgePanel;
/**
* Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -248,18 +208,6 @@
}
};
- private final OnTouchListener mEdgePanelTouchListener = new OnTouchListener() {
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getActionMasked() == ACTION_DOWN) {
- mWindowHitTarget = v == mLeftEdgePanel ? WINDOW_TARGET_LEFT : WINDOW_TARGET_RIGHT;
- mDownHitTarget = HIT_TARGET_NONE;
- }
- return mGestureHelper.onTouchEvent(event);
- }
- };
-
private final AccessibilityDelegate mQuickStepAccessibilityDelegate
= new AccessibilityDelegate() {
private AccessibilityAction mToggleOverviewAction;
@@ -286,104 +234,10 @@
}
};
- // TODO(b/112934365): To be removed
- private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() {
- @Override
- public void onGestureRemap(int[] actions) {
- updateNavigationGestures();
- }
-
- @Override
- public void onBackButtonVisibilityChanged(boolean visible) {
- if (!inScreenPinning()) {
- getBackButton().setVisibility(QuickStepController.shouldHideBackButton(getContext())
- ? GONE : VISIBLE);
- }
- }
-
- @Override
- public void onHomeButtonVisibilityChanged(boolean visible) {
- getHomeButton().setVisibility(QuickStepController.shouldHideHomeButton(getContext())
- ? GONE : VISIBLE);
- }
-
- @Override
- public void onColorAdaptChanged(boolean enabled) {
- if (enabled) {
- mTintController.start();
- } else {
- mTintController.stop();
- }
- }
-
- @Override
- public void onEdgeSensitivityChanged(int width, int height) {
- if (mLeftEdgePanel != null) {
- mLeftEdgePanel.setDimensions(width, height);
- }
- if (mRightEdgePanel != null) {
- mRightEdgePanel.setDimensions(width, height);
- }
- }
-
- @Override
- public void onHomeHandleVisiblilityChanged(boolean visible) {
- showHomeHandle(QuickStepController.showHomeHandle(getContext()));
- }
-
- @Override
- public void onAssistantGestureEnabled(boolean enabled) {
- updateAssistantAvailability();
- }
- };
-
- private final IPinnedStackListener.Stub mImeChangedListener = new IPinnedStackListener.Stub() {
- @Override
- public void onListenerRegistered(IPinnedStackController controller) {
- }
-
- @Override
- public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- post(() -> {
- // When the ime changes visibility, resize the edge panels to not cover the ime
- final int width = mPrototypeController.getEdgeSensitivityWidth();
- int height = mContext.getDisplay().getHeight() - imeHeight;
- if (!imeVisible) {
- // Hide the navigation bar area at the bottom for gestures
- height -= getResources().getDimensionPixelOffset(R.dimen.navigation_bar_height);
- }
- if (mLeftEdgePanel != null) {
- mLeftEdgePanel.setDimensions(width, height);
- }
- if (mRightEdgePanel != null) {
- mRightEdgePanel.setDimensions(width, height);
- }
- });
- }
-
- @Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
- }
-
- @Override
- public void onMinimizedStateChanged(boolean isMinimized) {
- }
-
- @Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) {
- }
-
- @Override
- public void onActionsChanged(ParceledListSlice actions) {
- }
- };
-
private BroadcastReceiver mOverlaysChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- showHomeHandle(QuickStepController.showHomeHandle(getContext()));
+ onOverlaysChanged();
}
};
@@ -431,23 +285,9 @@
mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
mDeadZone = new DeadZone(this);
- mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
- mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
- mBackAction = new NavigationBackAction(this, mOverviewProxyService);
- mQuickSwitchAction = new QuickSwitchAction(this, mOverviewProxyService);
- mDefaultGestureMap = new NavigationGestureAction[] {
- mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
- mQuickScrubAction, null /* swipeLeftEdgeAction */, null /* swipeRightEdgeAction */
- };
-
- mPrototypeController = new NavigationPrototypeController(context);
- mPrototypeController.register();
- mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
+ mEdgeBackGestureHandler = new EdgeBackGestureHandler(context);
mTintController = new NavBarTintController(this, getLightTransitionsController());
- IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
- filter.addDataScheme("package");
- context.registerReceiver(mOverlaysChangedReceiver, filter);
}
public NavBarTintController getTintController() {
@@ -464,14 +304,6 @@
public void setComponents(NotificationPanelView panel, AssistManager assistManager) {
mPanelView = panel;
- if (mAssistantAction == null) {
- mAssistantAction = new NavigationAssistantAction(this, mOverviewProxyService,
- assistManager);
- }
- if (mGestureHelper instanceof QuickStepController) {
- ((QuickStepController) mGestureHelper).setComponents(this);
- updateNavigationGestures();
- }
}
@Override
@@ -480,44 +312,6 @@
mTintController.onDraw();
}
- private void updateNavigationGestures() {
- if (mGestureHelper instanceof QuickStepController) {
- // TODO: Clarify this when we remove the prototype controller
- final int[] gesturalMap = {0, 7, 1, 1, 3, 3};
- final int[] normalMap = {0, 0, 0, 0, 0, 0};
- final int[] assignedMap = QuickStepContract.isGesturalMode(getContext())
- ? gesturalMap
- : normalMap;
- ((QuickStepController) mGestureHelper).setGestureActions(
- getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
- getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
- getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
- getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]),
- getNavigationActionFromType(assignedMap[4], mDefaultGestureMap[4]),
- getNavigationActionFromType(assignedMap[5], mDefaultGestureMap[5]));
- }
- }
-
- private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType,
- NavigationGestureAction defaultAction) {
- switch(actionType) {
- case NavigationPrototypeController.ACTION_QUICKSTEP:
- return mQuickStepAction;
- case NavigationPrototypeController.ACTION_QUICKSCRUB:
- return mQuickScrubAction;
- case NavigationPrototypeController.ACTION_BACK:
- return mBackAction;
- case NavigationPrototypeController.ACTION_QUICKSWITCH:
- return mQuickSwitchAction;
- case NavigationPrototypeController.ACTION_ASSISTANT:
- return mAssistantAction;
- case NavigationPrototypeController.ACTION_NOTHING:
- return null;
- default:
- return defaultAction;
- }
- }
-
public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
mOnVerticalChangedListener = onVerticalChangedListener;
notifyVerticalChangedListener(mIsVertical);
@@ -525,28 +319,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- final boolean deadZoneConsumed = shouldDeadZoneConsumeTouchEvents(event);
- switch (event.getActionMasked()) {
- case ACTION_DOWN:
- int x = (int) event.getX();
- int y = (int) event.getY();
- mDownHitTarget = HIT_TARGET_NONE;
- mWindowHitTarget = WINDOW_TARGET_BOTTOM;
- if (deadZoneConsumed) {
- mDownHitTarget = HIT_TARGET_DEAD_ZONE;
- } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
- mDownHitTarget = HIT_TARGET_BACK;
- } else if (getHomeButton().isVisible() && mHomeButtonBounds.contains(x, y)) {
- mDownHitTarget = HIT_TARGET_HOME;
- } else if (getRecentsButton().isVisible() && mRecentsButtonBounds.contains(x, y)) {
- mDownHitTarget = HIT_TARGET_OVERVIEW;
- } else if (getRotateSuggestionButton().isVisible()
- && mRotationButtonBounds.contains(x, y)) {
- mDownHitTarget = HIT_TARGET_ROTATION;
- }
- break;
- }
- return mGestureHelper.onInterceptTouchEvent(event);
+ return shouldDeadZoneConsumeTouchEvents(event) || super.onInterceptTouchEvent(event);
}
@Override
@@ -570,8 +343,12 @@
}
private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mDeadZoneConsuming = false;
+ }
if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) {
- switch (event.getActionMasked()) {
+ switch (action) {
case MotionEvent.ACTION_DOWN:
// Allow gestures starting in the deadzone to be slippery
setSlippery(true);
@@ -589,14 +366,6 @@
return false;
}
- public @NavigationBarCompat.HitTarget int getDownHitTarget() {
- return mDownHitTarget;
- }
-
- public @WindowTarget int getWindowTarget() {
- return mWindowHitTarget;
- }
-
public void abortCurrentGesture() {
getHomeButton().abortCurrentGesture();
}
@@ -654,13 +423,6 @@
return mOverviewProxyService.shouldShowSwipeUpUI() && isOverviewEnabled();
}
- public boolean isQuickScrubEnabled() {
- // TODO(b/112934365): Remove this sys prop flag
- return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
- && mOverviewProxyService.isEnabled() && isOverviewEnabled()
- && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0);
- }
-
private void reloadNavIcons() {
updateIcons(Configuration.EMPTY);
}
@@ -802,7 +564,7 @@
mBarTransitions.reapplyDarkIntensity();
- boolean disableHome = QuickStepController.shouldHideHomeButton(getContext())
+ boolean disableHome = QuickStepContract.isGesturalMode(getContext())
|| ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
// TODO(b/113914868): investigation log for disappearing home button
@@ -812,7 +574,7 @@
// Always disable recents when alternate car mode UI is active and for secondary displays.
boolean disableRecent = isRecentsButtonDisabled();
- boolean disableBack = QuickStepController.shouldHideBackButton(getContext())
+ boolean disableBack = QuickStepContract.isGesturalMode(getContext())
|| (((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) && !useAltBack);
// When screen pinning, don't hide back and home when connected service or back and
@@ -921,10 +683,6 @@
final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI();
if (mNavigationInflaterView != null) {
- if (mPrototypeController.showHomeHandle()) {
- showHomeHandle(true /* visible */);
- }
-
// Reinflate the navbar if needed, no-op unless the swipe up state changes
mNavigationInflaterView.onLikelyDefaultLayoutChange();
}
@@ -957,16 +715,6 @@
setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery);
}
- public void setWindowTouchable(boolean flag) {
- setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag);
- if (mLeftEdgePanel != null) {
- mLeftEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag);
- }
- if (mRightEdgePanel != null) {
- mRightEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag);
- }
- }
-
private void setWindowFlag(int flags, boolean enable) {
final ViewGroup navbarView = ((ViewGroup) getParent());
if (navbarView == null) {
@@ -985,15 +733,16 @@
wm.updateViewLayout(navbarView, lp);
}
- private void showHomeHandle(boolean visible) {
+ private void onOverlaysChanged() {
mNavigationInflaterView.onTuningChanged(NAV_BAR_VIEWS, null);
// Color adaption is tied with showing home handle, only avaliable if visible
- if (visible) {
+ if (QuickStepContract.isGesturalMode(getContext())) {
mTintController.start();
} else {
mTintController.stop();
}
+ mEdgeBackGestureHandler.onOverlaysChanged();
}
public void setAssistantAvailable(boolean available) {
@@ -1186,17 +935,6 @@
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get nav bar position.", e);
}
-
- // For landscape, hide the panel that would interfere with navigation bar layout
- if (mLeftEdgePanel != null && mRightEdgePanel != null) {
- mLeftEdgePanel.setVisibility(VISIBLE);
- mRightEdgePanel.setVisibility(VISIBLE);
- if (navBarPos == NAV_BAR_LEFT) {
- mLeftEdgePanel.setVisibility(GONE);
- } else if (navBarPos == NAV_BAR_RIGHT) {
- mRightEdgePanel.setVisibility(GONE);
- }
- }
mGestureHelper.setBarState(isRtl, navBarPos);
}
@@ -1320,27 +1058,10 @@
NavGesture.class, false /* Only one */);
setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
- if (QuickStepContract.isGesturalMode(getContext())) {
- WindowManager wm = (WindowManager) getContext()
- .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.LEFT | Gravity.TOP);
- mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
- Gravity.RIGHT | Gravity.TOP);
- mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
- mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
- wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());
- wm.addView(mRightEdgePanel, mRightEdgePanel.getLayoutParams());
-
- try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(mImeChangedListener);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register pinned stack listener", e);
- }
- }
+ IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+ filter.addDataScheme("package");
+ getContext().registerReceiver(mOverlaysChangedReceiver, filter);
+ mEdgeBackGestureHandler.onNavBarAttached();
}
@Override
@@ -1350,21 +1071,13 @@
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
- mPrototypeController.unregister();
- getContext().unregisterReceiver(mOverlaysChangedReceiver);
setUpSwipeUpOnboarding(false);
for (int i = 0; i < mButtonDispatchers.size(); ++i) {
mButtonDispatchers.valueAt(i).onDestroy();
}
- WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
- if (mLeftEdgePanel != null) {
- wm.removeView(mLeftEdgePanel);
- }
- if (mRightEdgePanel != null) {
- wm.removeView(mRightEdgePanel);
- }
- WindowManagerWrapper.getInstance().removePinnedStackListener(mImeChangedListener);
+ getContext().unregisterReceiver(mOverlaysChangedReceiver);
+ mEdgeBackGestureHandler.onNavBarDetached();
}
private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) {
@@ -1383,12 +1096,10 @@
@Override
public void onPluginDisconnected(NavGesture plugin) {
- QuickStepController defaultHelper = new QuickStepController(getContext());
- defaultHelper.setComponents(this);
if (mGestureHelper != null) {
mGestureHelper.destroy();
+ mGestureHelper = null;
}
- mGestureHelper = defaultHelper;
updateTaskSwitchHelper();
}
@@ -1431,14 +1142,6 @@
mContextualButtonGroup.dump(pw);
if (mGestureHelper != null) {
- pw.println("Navigation Gesture Actions {");
- pw.print(" "); pw.println("QuickScrub Enabled=" + mQuickScrubAction.isEnabled());
- pw.print(" "); pw.println("QuickScrub Active=" + mQuickScrubAction.isActive());
- pw.print(" "); pw.println("QuickStep Enabled=" + mQuickStepAction.isEnabled());
- pw.print(" "); pw.println("QuickStep Active=" + mQuickStepAction.isActive());
- pw.print(" "); pw.println("Back Gesture Enabled=" + mBackAction.isEnabled());
- pw.print(" "); pw.println("Back Gesture Active=" + mBackAction.isActive());
- pw.println("}");
mGestureHelper.dump(pw);
}
mRecentsOnboarding.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
deleted file mode 100644
index eca14eb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.view.MotionEvent;
-
-import com.android.systemui.recents.OverviewProxyService;
-
-/**
- * A gesture action that would be triggered and reassigned by {@link QuickStepController}
- */
-public abstract class NavigationGestureAction {
- protected final NavigationBarView mNavigationBarView;
- protected final OverviewProxyService mProxySender;
-
- protected int mNavigationBarPosition;
- protected boolean mDragHorizontalPositive;
- protected boolean mDragVerticalPositive;
- private boolean mIsActive;
-
- public NavigationGestureAction(@NonNull NavigationBarView navigationBarView,
- @NonNull OverviewProxyService service) {
- mNavigationBarView = navigationBarView;
- mProxySender = service;
- }
-
- /**
- * Pass event that the state of the bar (such as rotation) has changed
- * @param changed if rotation or drag positive direction (such as ltr) has changed
- * @param navBarPos position of navigation bar
- * @param dragHorPositive direction of positive horizontal drag, could change with ltr changes
- * @param dragVerPositive direction of positive vertical drag, could change with ltr changes
- */
- public void setBarState(boolean changed, int navBarPos, boolean dragHorPositive,
- boolean dragVerPositive) {
- mNavigationBarPosition = navBarPos;
- mDragHorizontalPositive = dragHorPositive;
- mDragVerticalPositive = dragVerPositive;
- }
-
- /**
- * Resets the state of the action. Called when touch down occurs over the Navigation Bar.
- */
- public void reset() {
- mIsActive = false;
- }
-
- /**
- * Start the gesture and the action will be active
- * @param event the event that caused the gesture
- */
- public void startGesture(MotionEvent event) {
- mIsActive = true;
- onGestureStart(event);
- }
-
- /**
- * Gesture has ended with action cancel or up and this action will not be active
- */
- public void endGesture() {
- mIsActive = false;
- onGestureEnd();
- }
-
- /**
- * If the action is currently active based on the gesture that triggered it. Only one action
- * can occur at a time
- * @return whether or not if this action has been triggered
- */
- public boolean isActive() {
- return mIsActive;
- }
-
- /**
- * @return whether or not this action can run if notification shade is shown
- */
- public boolean canRunWhenNotificationsShowing() {
- return true;
- }
-
- /**
- * @return whether or not this action triggers when starting a gesture from a certain hit target
- * If {@link HIT_TARGET_NONE} is specified then action does not need to be triggered by button
- */
- public int requiresTouchDownHitTarget() {
- return HIT_TARGET_NONE;
- }
-
- /**
- * @return whether or not to move the button that started gesture over with user input drag
- */
- public boolean allowHitTargetToMoveOverDrag() {
- return false;
- }
-
- /**
- * Tell if the action is able to execute. Note that {@link #isEnabled()} must be true for this
- * to be checked. The difference between this and {@link #isEnabled()} is that this dependent
- * on the state of the navigation bar
- * @return true if action can execute after gesture activates based on current states
- */
- public boolean canPerformAction() {
- return true;
- }
-
- /**
- * Decide if the controller should not send the current motion event to launcher via
- * {@link OverviewProxyService}
- * @return if controller should not proxy
- */
- public boolean disableProxyEvents() {
- return false;
- }
-
- /**
- * Tell if action is enabled. Compared to {@link #canPerformAction()} this is based on settings
- * if the action is disabled for a particular gesture. For example a back action can be enabled
- * however if there is nothing to back to then {@link #canPerformAction()} should return false.
- * In this way if the action requires {@link #allowHitTargetToMoveOverDrag()} then if enabled,
- * the button can be dragged with a large dampening factor during the gesture but will not
- * activate the action.
- * @return true if this action is enabled and can run
- */
- public abstract boolean isEnabled();
-
- protected void onDarkIntensityChange(float intensity) {
- }
-
- protected void onDraw(Canvas canvas) {
- }
-
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- }
-
- /**
- * When gesture starts, this will run to execute the action
- * @param event the event that triggered the gesture
- */
- protected abstract void onGestureStart(MotionEvent event);
-
- /**
- * Channels motion move events to the action to track the user inputs
- * @param x the x position
- * @param y the y position
- */
- public void onGestureMove(int x, int y) {
- }
-
- /**
- * When gesture ends, this will run from action up or cancel
- */
- protected void onGestureEnd() {
- }
-
- protected Context getContext() {
- return mNavigationBarView.getContext();
- }
-
- protected boolean isNavBarVertical() {
- return mNavigationBarPosition == NAV_BAR_LEFT || mNavigationBarPosition == NAV_BAR_RIGHT;
- }
-
- protected boolean getGlobalBoolean(@NonNull String key) {
- return QuickStepController.getBoolGlobalSetting(getContext(), key);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 02bad73..cff3855 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -152,9 +152,11 @@
private KeyguardUserSwitcher mKeyguardUserSwitcher;
@VisibleForTesting
protected KeyguardStatusBarView mKeyguardStatusBar;
- private ViewGroup mBigClockContainer;
+ @VisibleForTesting
+ protected ViewGroup mBigClockContainer;
private QS mQs;
- private FrameLayout mQsFrame;
+ @VisibleForTesting
+ protected FrameLayout mQsFrame;
@VisibleForTesting
protected KeyguardStatusView mKeyguardStatusView;
private View mQsNavbarScrim;
@@ -266,6 +268,7 @@
private int mIndicationBottomPadding;
private int mAmbientIndicationBottomPadding;
private boolean mIsFullWidth;
+ private boolean mBlockingExpansionForCurrentTouch;
/**
* Current dark amount that follows regular interpolation curve of animation.
@@ -983,6 +986,11 @@
return false;
}
initDownStates(event);
+ // Make sure the next touch won't the blocked after the current ends.
+ if (event.getAction() == MotionEvent.ACTION_UP
+ || event.getAction() == MotionEvent.ACTION_CANCEL) {
+ mBlockingExpansionForCurrentTouch = false;
+ }
if (!mIsExpanding && mPulseExpansionHandler.onTouchEvent(event)) {
// We're expanding all the other ones shouldn't get this anymore
return true;
@@ -1662,7 +1670,7 @@
if (!mQsExpansionEnabled || mCollapsedOnDown) {
return false;
}
- View header = mKeyguardShowing ? mKeyguardStatusBar : mQs.getHeader();
+ View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
final boolean onHeader = x >= mQsFrame.getX()
&& x <= mQsFrame.getX() + mQsFrame.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
@@ -2337,7 +2345,7 @@
@Override
protected boolean isTrackingBlocked() {
- return mConflictingQsExpansionGesture && mQsExpanded;
+ return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
}
public boolean isQsExpanded() {
@@ -2937,6 +2945,14 @@
updateLockIcon();
}
+ /**
+ * Do not let the user drag the shade up and down for the current touch session.
+ * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
+ */
+ public void blockExpansionForCurrentTouch() {
+ mBlockingExpansionForCurrentTouch = mTracking;
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dump(fd, pw, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
deleted file mode 100644
index bbfd51a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.Interpolators.ALPHA_IN;
-import static com.android.systemui.Interpolators.ALPHA_OUT;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.NonNull;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RadialGradient;
-import android.graphics.Shader;
-import android.util.FloatProperty;
-import android.view.MotionEvent;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.recents.utilities.Utilities;
-
-/**
- * QuickScrub action to send to launcher to start quickscrub gesture
- */
-public class QuickScrubAction extends QuickSwitchAction {
- private static final String TAG = "QuickScrubAction";
-
- private static final float TRACK_SCALE = 0.95f;
- private static final float GRADIENT_WIDTH = .75f;
- private static final int ANIM_IN_DURATION_MS = 150;
- private static final int ANIM_OUT_DURATION_MS = 134;
-
- private AnimatorSet mTrackAnimator;
- private View mCurrentNavigationBarView;
-
- private float mTrackScale = TRACK_SCALE;
- private float mTrackAlpha;
- private float mHighlightCenter;
- private float mDarkIntensity;
-
- private final int mTrackThickness;
- private final int mTrackEndPadding;
- private final Paint mTrackPaint = new Paint();
-
- private final FloatProperty<QuickScrubAction> mTrackAlphaProperty =
- new FloatProperty<QuickScrubAction>("TrackAlpha") {
- @Override
- public void setValue(QuickScrubAction action, float alpha) {
- mTrackAlpha = alpha;
- mNavigationBarView.invalidate();
- }
-
- @Override
- public Float get(QuickScrubAction action) {
- return mTrackAlpha;
- }
- };
-
- private final FloatProperty<QuickScrubAction> mTrackScaleProperty =
- new FloatProperty<QuickScrubAction>("TrackScale") {
- @Override
- public void setValue(QuickScrubAction action, float scale) {
- mTrackScale = scale;
- mNavigationBarView.invalidate();
- }
-
- @Override
- public Float get(QuickScrubAction action) {
- return mTrackScale;
- }
- };
-
- private final FloatProperty<QuickScrubAction> mNavBarAlphaProperty =
- new FloatProperty<QuickScrubAction>("NavBarAlpha") {
- @Override
- public void setValue(QuickScrubAction action, float alpha) {
- if (mCurrentNavigationBarView != null) {
- mCurrentNavigationBarView.setAlpha(alpha);
- }
- }
-
- @Override
- public Float get(QuickScrubAction action) {
- if (mCurrentNavigationBarView != null) {
- return mCurrentNavigationBarView.getAlpha();
- }
- return 1f;
- }
- };
-
- private AnimatorListenerAdapter mQuickScrubEndListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCurrentNavigationBarView != null) {
- mCurrentNavigationBarView.setAlpha(1f);
- }
- mCurrentNavigationBarView = null;
- updateHighlight();
- }
- };
-
- public QuickScrubAction(@NonNull NavigationBarView navigationBarView,
- @NonNull OverviewProxyService service) {
- super(navigationBarView, service);
- mTrackPaint.setAntiAlias(true);
- mTrackPaint.setDither(true);
-
- final Resources res = navigationBarView.getResources();
- mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
- mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
- }
-
- @Override
- public void setBarState(boolean changed, int navBarPos, boolean dragHorPositive,
- boolean dragVerPositive) {
- super.setBarState(changed, navBarPos, dragHorPositive, dragVerPositive);
- if (changed && isActive()) {
- // End quickscrub if the state changes mid-transition
- endQuickScrub(false /* animate */);
- }
- }
-
- @Override
- public void reset() {
- super.reset();
-
- // End any existing quickscrub animations before starting the new transition
- if (mTrackAnimator != null) {
- mTrackAnimator.end();
- mTrackAnimator = null;
- }
- mCurrentNavigationBarView = mNavigationBarView.getCurrentView();
- }
-
- @Override
- public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final int paddingLeft = mNavigationBarView.getPaddingLeft();
- final int paddingTop = mNavigationBarView.getPaddingTop();
- final int paddingRight = mNavigationBarView.getPaddingRight();
- final int paddingBottom = mNavigationBarView.getPaddingBottom();
- final int width = (right - left) - paddingRight - paddingLeft;
- final int height = (bottom - top) - paddingBottom - paddingTop;
- final int x1, x2, y1, y2;
- if (isNavBarVertical()) {
- x1 = (width - mTrackThickness) / 2 + paddingLeft;
- x2 = x1 + mTrackThickness;
- y1 = paddingTop + mTrackEndPadding;
- y2 = y1 + height - 2 * mTrackEndPadding;
- } else {
- y1 = (height - mTrackThickness) / 2 + paddingTop;
- y2 = y1 + mTrackThickness;
- x1 = mNavigationBarView.getPaddingStart() + mTrackEndPadding;
- x2 = x1 + width - 2 * mTrackEndPadding;
- }
- mDragOverRect.set(x1, y1, x2, y2);
- }
-
- @Override
- public void onDarkIntensityChange(float intensity) {
- mDarkIntensity = intensity;
- updateHighlight();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- if (!isEnabled()) {
- return;
- }
- mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
-
- // Scale the track, but apply the inverse scale from the nav bar
- final float radius = mDragOverRect.height() / 2;
- canvas.save();
- float translate = Utilities.clamp(mHighlightCenter, mDragOverRect.left,
- mDragOverRect.right);
- canvas.translate(translate, 0);
- canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
- 1f / mNavigationBarView.getScaleY(),
- mDragOverRect.centerX(), mDragOverRect.centerY());
- canvas.drawRoundRect(mDragOverRect.left - translate, mDragOverRect.top,
- mDragOverRect.right - translate, mDragOverRect.bottom, radius, radius, mTrackPaint);
- canvas.restore();
- }
-
- @Override
- public boolean isEnabled() {
- return mNavigationBarView.isQuickScrubEnabled();
- }
-
- @Override
- protected void onGestureStart(MotionEvent event) {
- updateHighlight();
- ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 1f),
- PropertyValuesHolder.ofFloat(mTrackScaleProperty, 1f));
- trackAnimator.setInterpolator(ALPHA_IN);
- trackAnimator.setDuration(ANIM_IN_DURATION_MS);
- ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 0f);
- navBarAnimator.setInterpolator(ALPHA_OUT);
- navBarAnimator.setDuration(ANIM_OUT_DURATION_MS);
- mTrackAnimator = new AnimatorSet();
- mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
- mTrackAnimator.start();
-
- startQuickGesture(event);
- }
-
- @Override
- public void onGestureMove(int x, int y) {
- super.onGestureMove(x, y);
- mHighlightCenter = x;
- mNavigationBarView.invalidate();
- }
-
- @Override
- protected void onGestureEnd() {
- endQuickScrub(true /* animate */);
- }
-
- private void endQuickScrub(boolean animate) {
- animateEnd();
- endQuickGesture(animate);
- if (!animate) {
- if (mTrackAnimator != null) {
- mTrackAnimator.end();
- mTrackAnimator = null;
- }
- }
- }
-
- private void updateHighlight() {
- if (mDragOverRect.isEmpty()) {
- return;
- }
- int colorBase, colorGrad;
- if (mDarkIntensity > 0.5f) {
- colorBase = getContext().getColor(R.color.quick_step_track_background_background_dark);
- colorGrad = getContext().getColor(R.color.quick_step_track_background_foreground_dark);
- } else {
- colorBase = getContext().getColor(R.color.quick_step_track_background_background_light);
- colorGrad = getContext().getColor(R.color.quick_step_track_background_foreground_light);
- }
- final RadialGradient mHighlight = new RadialGradient(0, mDragOverRect.height() / 2,
- mDragOverRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
- Shader.TileMode.CLAMP);
- mTrackPaint.setShader(mHighlight);
- }
-
- private void animateEnd() {
- if (mTrackAnimator != null) {
- mTrackAnimator.cancel();
- }
-
- ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 0f),
- PropertyValuesHolder.ofFloat(mTrackScaleProperty, TRACK_SCALE));
- trackAnimator.setInterpolator(ALPHA_OUT);
- trackAnimator.setDuration(ANIM_OUT_DURATION_MS);
- ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 1f);
- navBarAnimator.setInterpolator(ALPHA_IN);
- navBarAnimator.setDuration(ANIM_IN_DURATION_MS);
- mTrackAnimator = new AnimatorSet();
- mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
- mTrackAnimator.addListener(mQuickScrubEndListener);
- mTrackAnimator.start();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
deleted file mode 100644
index b18b79e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepAction.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.systemui.recents.OverviewProxyService;
-
-/**
- * QuickStep action to send to launcher to start overview
- */
-public class QuickStepAction extends NavigationGestureAction {
- private static final String TAG = "QuickStepAction";
-
- public QuickStepAction(@NonNull NavigationBarView navigationBarView,
- @NonNull OverviewProxyService service) {
- super(navigationBarView, service);
- }
-
- @Override
- public boolean canRunWhenNotificationsShowing() {
- return false;
- }
-
- @Override
- public boolean isEnabled() {
- return mNavigationBarView.isQuickStepSwipeUpEnabled();
- }
-
- @Override
- public void onGestureStart(MotionEvent event) {
- try {
- mProxySender.getProxy().onQuickStep(event);
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Step Start");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send quick step started.", e);
- }
- mProxySender.notifyQuickStepStarted();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
deleted file mode 100644
index 8053ec7..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ /dev/null
@@ -1,731 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
-import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.hardware.input.InputManager;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
-import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import java.io.PrintWriter;
-
-/**
- * Class to detect gestures on the navigation bar and implement quick scrub.
- * Note that the variables in this class horizontal and vertical represents horizontal always
- * aligned with along the navigation bar.
- */
-public class QuickStepController implements GestureHelper {
-
- private static final String TAG = "QuickStepController";
-
- /** Experiment to swipe home button left to execute a back key press */
- private static final String HIDE_BACK_BUTTON_PROP = "quickstepcontroller_hideback";
- private static final String HIDE_HOME_BUTTON_PROP = "quickstepcontroller_hidehome";
- private static final String ENABLE_CLICK_THROUGH_NAV_PROP = "quickstepcontroller_clickthrough";
- private static final String GESTURE_REGION_THRESHOLD_SETTING = "gesture_region_threshold";
- private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
- private static final long CLICK_THROUGH_TAP_DELAY = 70;
- private static final long CLICK_THROUGH_TAP_RESET_DELAY = 100;
-
- /** When the home-swipe-back gesture is disallowed, make it harder to pull */
- private static final float HORIZONTAL_GESTURE_DAMPING = 0.3f;
- private static final float VERTICAL_GESTURE_DAMPING = 0.15f;
- private static final float HORIZONTAL_DISABLED_GESTURE_DAMPING = 0.16f;
- private static final float VERTICAL_DISABLED_GESTURE_DAMPING = 0.06f;
-
- private static final int ACTION_SWIPE_UP_INDEX = 0;
- private static final int ACTION_SWIPE_DOWN_INDEX = 1;
- private static final int ACTION_SWIPE_LEFT_INDEX = 2;
- private static final int ACTION_SWIPE_RIGHT_INDEX = 3;
- private static final int ACTION_SWIPE_LEFT_FROM_EDGE_INDEX = 4;
- private static final int ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX = 5;
- private static final int MAX_GESTURES = 6;
-
- private NavigationBarView mNavigationBarView;
-
- private boolean mAllowGestureDetection;
- private boolean mNotificationsVisibleOnDown;
- private int mTouchDownX;
- private int mTouchDownY;
- private boolean mDragHPositive;
- private boolean mDragVPositive;
- private boolean mIsRTL;
- private int mNavBarPosition;
- private float mDarkIntensity;
- private ViewPropertyAnimator mDragBtnAnimator;
- private ButtonDispatcher mHitTarget;
- private boolean mIsInScreenPinning;
- private boolean mGestureHorizontalDragsButton;
- private boolean mGestureVerticalDragsButton;
- private float mMaxDragLimit;
- private float mMinDragLimit;
- private float mDragDampeningFactor;
- private boolean mClickThroughPressed;
- private float mClickThroughPressX;
- private float mClickThroughPressY;
- private int mGestureRegionThreshold;
-
- private NavigationGestureAction mCurrentAction;
- private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
-
- private final Rect mLastLayoutRect = new Rect();
- private final OverviewProxyService mOverviewEventSender;
- private final Context mContext;
- private final StatusBar mStatusBar;
- private final Matrix mTransformGlobalMatrix = new Matrix();
- private final Matrix mTransformLocalMatrix = new Matrix();
-
- public QuickStepController(Context context) {
- mContext = context;
- mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
- mOverviewEventSender = Dependency.get(OverviewProxyService.class);
- }
-
- private final Runnable mClickThroughSendTap = new Runnable() {
- @Override
- public void run() {
- sendTap(mClickThroughPressX, mClickThroughPressY);
- mNavigationBarView.postDelayed(mClickThroughResetTap, CLICK_THROUGH_TAP_RESET_DELAY);
- }
- };
-
- private final Runnable mClickThroughResetTap = () -> {
- mNavigationBarView.setWindowTouchable(true);
- mClickThroughPressed = false;
- };
-
- public void setComponents(NavigationBarView navigationBarView) {
- mNavigationBarView = navigationBarView;
-
- mNavigationBarView.getBackButton().setVisibility(shouldHideBackButton(mContext)
- ? View.GONE
- : View.VISIBLE);
- }
-
- /**
- * Set each gesture an action. After set the gestures triggered will run the actions attached.
- * @param swipeUpAction action after swiping up
- * @param swipeDownAction action after swiping down
- * @param swipeLeftAction action after swiping left
- * @param swipeRightAction action after swiping right
- * @param swipeLeftFromEdgeAction action swiping left starting from the right side
- * @param swipeRightFromEdgeAction action swiping right starting from the left side
- */
- public void setGestureActions(@Nullable NavigationGestureAction swipeUpAction,
- @Nullable NavigationGestureAction swipeDownAction,
- @Nullable NavigationGestureAction swipeLeftAction,
- @Nullable NavigationGestureAction swipeRightAction,
- @Nullable NavigationGestureAction swipeLeftFromEdgeAction,
- @Nullable NavigationGestureAction swipeRightFromEdgeAction) {
- mGestureActions[ACTION_SWIPE_UP_INDEX] = swipeUpAction;
- mGestureActions[ACTION_SWIPE_DOWN_INDEX] = swipeDownAction;
- mGestureActions[ACTION_SWIPE_LEFT_INDEX] = swipeLeftAction;
- mGestureActions[ACTION_SWIPE_RIGHT_INDEX] = swipeRightAction;
- mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] = swipeLeftFromEdgeAction;
- mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] = swipeRightFromEdgeAction;
-
- // Set the current state to all actions
- for (NavigationGestureAction action: mGestureActions) {
- if (action != null) {
- action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
- action.onDarkIntensityChange(mDarkIntensity);
- action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top,
- mLastLayoutRect.right, mLastLayoutRect.bottom);
- }
- }
- }
-
- /**
- * @return true if we want to intercept touch events for quick scrub and prevent proxying the
- * event to the overview service.
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return handleTouchEvent(event);
- }
-
- /**
- * @return true if we want to handle touch events for quick scrub or if down event (that will
- * get consumed and ignored). No events will be proxied to the overview service.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // The same down event was just sent on intercept and therefore can be ignored here
- final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
- && mOverviewEventSender.getProxy() != null
- && mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM;
- return ignoreProxyDownEvent || handleTouchEvent(event);
- }
-
- private boolean handleTouchEvent(MotionEvent event) {
- final boolean deadZoneConsumed =
- mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
-
- // Requires proxy and an active gesture or able to perform any gesture to continue
- if (mOverviewEventSender.getProxy() == null
- || !mOverviewEventSender.shouldShowSwipeUpUI()
- || (mCurrentAction == null && !canPerformAnyAction())) {
- return deadZoneConsumed;
- }
- mNavigationBarView.requestUnbufferedDispatch(event);
-
- int action = event.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- int x = (int) event.getX();
- int y = (int) event.getY();
- mIsInScreenPinning = mNavigationBarView.inScreenPinning();
-
- for (NavigationGestureAction gestureAction: mGestureActions) {
- if (gestureAction != null) {
- gestureAction.reset();
- }
- }
-
- // Valid buttons to drag over
- switch (mNavigationBarView.getDownHitTarget()) {
- case HIT_TARGET_BACK:
- mHitTarget = mNavigationBarView.getBackButton();
- break;
- case HIT_TARGET_HOME:
- mHitTarget = mNavigationBarView.getHomeButton();
- break;
- case HIT_TARGET_OVERVIEW:
- mHitTarget = mNavigationBarView.getRecentsButton();
- break;
- default:
- mHitTarget = null;
- break;
- }
- if (mHitTarget != null) {
- // Pre-emptively delay the touch feedback for the button that we just touched
- mHitTarget.setDelayTouchFeedback(true);
- }
- mTouchDownX = x;
- mTouchDownY = y;
- mGestureHorizontalDragsButton = false;
- mGestureVerticalDragsButton = false;
- mTransformGlobalMatrix.set(Matrix.IDENTITY_MATRIX);
- mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
- mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
- mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
- mAllowGestureDetection = true;
- mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
- mGestureRegionThreshold = QuickStepContract.getEdgeSensitivityWidth(mContext);
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- if (!mAllowGestureDetection
- || mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) {
- break;
- }
- int x = (int) event.getX();
- int y = (int) event.getY();
- int xDiff = Math.abs(x - mTouchDownX);
- int yDiff = Math.abs(y - mTouchDownY);
-
- boolean exceededSwipeHorizontalTouchSlop, exceededSwipeVerticalTouchSlop,
- exceededSwipeVerticalDragSlop;
- int posH, touchDownH, posV, touchDownV;
-
- if (isNavBarVertical()) {
- exceededSwipeHorizontalTouchSlop =
- yDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && yDiff > xDiff;
- exceededSwipeVerticalTouchSlop =
- xDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && xDiff > yDiff;
- exceededSwipeVerticalDragSlop =
- xDiff > NavigationBarCompat.getQuickStepDragSlopPx() && xDiff > yDiff;
- posH = y;
- touchDownH = mTouchDownY;
- posV = x;
- touchDownV = mTouchDownX;
- } else {
- exceededSwipeHorizontalTouchSlop =
- xDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && xDiff > yDiff;
- exceededSwipeVerticalTouchSlop =
- yDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && yDiff > xDiff;
- exceededSwipeVerticalDragSlop =
- yDiff > NavigationBarCompat.getQuickStepDragSlopPx() && yDiff > xDiff;
- posH = x;
- touchDownH = mTouchDownX;
- posV = y;
- touchDownV = mTouchDownY;
- }
-
- if (mCurrentAction != null) {
- // Gesture started, provide positions to the current action
- mCurrentAction.onGestureMove(x, y);
- } else {
- // Detect gesture and try to execute an action, only one can run at a time
- if (exceededSwipeVerticalTouchSlop || exceededSwipeVerticalDragSlop) {
- if (mDragVPositive ? (posV < touchDownV) : (posV > touchDownV)) {
- // Swipe up gesture must use the larger slop
- if (exceededSwipeVerticalTouchSlop) {
- // Swiping up gesture
- tryToStartGesture(mGestureActions[ACTION_SWIPE_UP_INDEX],
- false /* alignedWithNavBar */, event);
- }
- } else {
- // Swiping down gesture
- tryToStartGesture(mGestureActions[ACTION_SWIPE_DOWN_INDEX],
- false /* alignedWithNavBar */, event);
- }
- } else if (exceededSwipeHorizontalTouchSlop) {
- if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
- // Swiping left (rtl) gesture
- tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
- true /* alignedWithNavBar */, event);
- } else {
- // Swiping right (ltr) gesture
- tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
- true /* alignedWithNavBar */, event);
- }
- }
- }
-
- handleDragHitTarget(mGestureHorizontalDragsButton ? posH : posV,
- mGestureHorizontalDragsButton ? touchDownH : touchDownV);
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- if (mCurrentAction != null) {
- mCurrentAction.endGesture();
- } else if (action == MotionEvent.ACTION_UP) {
- if (canTriggerEdgeSwipe(event)) {
- int index = mNavigationBarView.getWindowTarget() == NAV_BAR_LEFT
- ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX
- : ACTION_SWIPE_LEFT_FROM_EDGE_INDEX;
- tryToStartGesture(mGestureActions[index], false /* alignedWithNavBar */,
- event);
- if (mCurrentAction != null) {
- mCurrentAction.endGesture();
- }
- } else if (QuickStepContract.isNavBarClickThrough(mContext)
- && !mClickThroughPressed) {
- // Enable click through functionality where no gesture has been detected and
- // not passed the drag slop so inject a touch event at the same location
- // after making the navigation bar window untouchable. After a some time,
- // the navigation bar will be able to take input events again
- float diffX = Math.abs(event.getX() - mTouchDownX);
- float diffY = Math.abs(event.getY() - mTouchDownY);
-
- if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx()
- && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) {
- mNavigationBarView.setWindowTouchable(false);
- mClickThroughPressX = event.getRawX();
- mClickThroughPressY = event.getRawY();
- mClickThroughPressed = true;
- mNavigationBarView.postDelayed(mClickThroughSendTap,
- CLICK_THROUGH_TAP_DELAY);
- }
- }
- }
-
- // Return the hit target back to its original position
- if (mHitTarget != null) {
- final View button = mHitTarget.getCurrentView();
- if (mGestureHorizontalDragsButton || mGestureVerticalDragsButton) {
- mDragBtnAnimator = button.animate().setDuration(BACK_BUTTON_FADE_IN_ALPHA)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- if (mGestureVerticalDragsButton ^ isNavBarVertical()) {
- mDragBtnAnimator.translationY(0);
- } else {
- mDragBtnAnimator.translationX(0);
- }
- mDragBtnAnimator.start();
- }
- }
- break;
- }
-
- if (shouldProxyEvents(action)) {
- proxyMotionEvents(event);
- }
-
- // Clear action when gesture and event proxy finishes
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- mCurrentAction = null;
- }
- return mCurrentAction != null || deadZoneConsumed;
- }
-
- private void handleDragHitTarget(int position, int touchDown) {
- // Drag the hit target if gesture action requires it
- if (mHitTarget != null && (mGestureVerticalDragsButton || mGestureHorizontalDragsButton)) {
- final View button = mHitTarget.getCurrentView();
- if (mDragBtnAnimator != null) {
- mDragBtnAnimator.cancel();
- mDragBtnAnimator = null;
- }
-
- // Clamp drag to the bounding box of the navigation bar
- float diff = (position - touchDown) * mDragDampeningFactor;
- diff = Utilities.clamp(diff, mMinDragLimit, mMaxDragLimit);
- if (mGestureVerticalDragsButton ^ isNavBarVertical()) {
- button.setTranslationY(diff);
- } else {
- button.setTranslationX(diff);
- }
- }
- }
-
- private boolean shouldProxyEvents(int action) {
- // Do not send events for side navigation bar panels
- if (mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) {
- return false;
- }
- final boolean actionValid = (mCurrentAction == null
- || !mCurrentAction.disableProxyEvents());
- if (actionValid && !mIsInScreenPinning) {
- // Allow down, cancel and up events, move and other events are passed if notifications
- // are not showing and disabled gestures (such as long press) are not executed
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- return true;
- default:
- return !mNotificationsVisibleOnDown && mAllowGestureDetection;
- }
- }
- return false;
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- if (mCurrentAction != null) {
- mCurrentAction.onDraw(canvas);
- }
- }
-
- @Override
- public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- for (NavigationGestureAction action: mGestureActions) {
- if (action != null) {
- action.onLayout(changed, left, top, right, bottom);
- }
- }
- mLastLayoutRect.set(left, top, right, bottom);
- }
-
- @Override
- public void onDarkIntensityChange(float intensity) {
- final float oldIntensity = mDarkIntensity;
- mDarkIntensity = intensity;
-
- // When in quick scrub, invalidate gradient if changing intensity from black to white and
- // vice-versa
- if (mCurrentAction != null && mNavigationBarView.isQuickScrubEnabled()
- && Math.round(intensity) != Math.round(oldIntensity)) {
- mCurrentAction.onDarkIntensityChange(mDarkIntensity);
- }
- mNavigationBarView.invalidate();
- }
-
- @Override
- public void setBarState(boolean isRTL, int navBarPosition) {
- final boolean changed = (mIsRTL != isRTL) || (mNavBarPosition != navBarPosition);
- mIsRTL = isRTL;
- mNavBarPosition = navBarPosition;
-
- // Determine the drag directions depending on location of nav bar
- switch (navBarPosition) {
- case NAV_BAR_LEFT:
- mDragHPositive = !isRTL;
- mDragVPositive = false;
- break;
- case NAV_BAR_RIGHT:
- mDragHPositive = isRTL;
- mDragVPositive = true;
- break;
- case NAV_BAR_BOTTOM:
- mDragHPositive = !isRTL;
- mDragVPositive = true;
- break;
- case NAV_BAR_INVALID:
- Log.e(TAG, "Invalid nav bar position");
- break;
- }
-
- for (NavigationGestureAction action: mGestureActions) {
- if (action != null) {
- action.setBarState(changed, mNavBarPosition, mDragHPositive, mDragVPositive);
- }
- }
- }
-
- @Override
- public void onNavigationButtonLongPress(View v) {
- mAllowGestureDetection = false;
- }
-
- @Override
- public void dump(PrintWriter pw) {
- pw.println("QuickStepController {");
- pw.print(" "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection);
- pw.print(" "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown);
- pw.print(" "); pw.println("mNavBarPosition=" + mNavBarPosition);
- pw.print(" "); pw.println("mIsRTL=" + mIsRTL);
- pw.print(" "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning);
- pw.println("}");
- }
-
- public NavigationGestureAction getCurrentAction() {
- return mCurrentAction;
- }
-
- private void tryToStartGesture(NavigationGestureAction action, boolean alignedWithNavBar,
- MotionEvent event) {
- if (action == null) {
- return;
- }
- if (mIsInScreenPinning) {
- mNavigationBarView.showPinningEscapeToast();
- mAllowGestureDetection = false;
- return;
- }
-
- // Start new action from gesture if is able to start and depending on notifications
- // visibility and starting touch down target. If the action is enabled, then also check if
- // can perform the action so that if action requires the button to be dragged, then the
- // gesture will have a large dampening factor and prevent action from running.
- final boolean validHitTarget = action.requiresTouchDownHitTarget() == HIT_TARGET_NONE
- || action.requiresTouchDownHitTarget() == mNavigationBarView.getDownHitTarget();
- if (mCurrentAction == null && validHitTarget && action.isEnabled()
- && (!mNotificationsVisibleOnDown || action.canRunWhenNotificationsShowing())) {
- if (action.canPerformAction()) {
- mCurrentAction = action;
- event.transform(mTransformGlobalMatrix);
- action.startGesture(event);
- event.transform(mTransformLocalMatrix);
-
- // Calculate the bounding limits of drag to avoid dragging off nav bar's window
- if (action.allowHitTargetToMoveOverDrag() && mHitTarget != null) {
- final int[] buttonCenter = new int[2];
- View button = mHitTarget.getCurrentView();
- button.getLocationInWindow(buttonCenter);
- buttonCenter[0] += button.getWidth() / 2;
- buttonCenter[1] += button.getHeight() / 2;
- final int x = isNavBarVertical() ? buttonCenter[1] : buttonCenter[0];
- final int y = isNavBarVertical() ? buttonCenter[0] : buttonCenter[1];
- final int iconHalfSize = mContext.getResources()
- .getDimensionPixelSize(R.dimen.navigation_icon_size) / 2;
-
- if (alignedWithNavBar) {
- mMinDragLimit = iconHalfSize - x;
- mMaxDragLimit = -x - iconHalfSize + (isNavBarVertical()
- ? mNavigationBarView.getHeight() : mNavigationBarView.getWidth());
- } else {
- mMinDragLimit = iconHalfSize - y;
- mMaxDragLimit = -y - iconHalfSize + (isNavBarVertical()
- ? mNavigationBarView.getWidth() : mNavigationBarView.getHeight());
- }
- }
- }
-
- // Handle direction of the hit target drag from the axis that started the gesture
- // Also calculate the dampening factor, weaker dampening if there is an active action
- if (action.allowHitTargetToMoveOverDrag()) {
- if (alignedWithNavBar) {
- mGestureHorizontalDragsButton = true;
- mGestureVerticalDragsButton = false;
- mDragDampeningFactor = action.isActive()
- ? HORIZONTAL_GESTURE_DAMPING : HORIZONTAL_DISABLED_GESTURE_DAMPING;
- } else {
- mGestureVerticalDragsButton = true;
- mGestureHorizontalDragsButton = false;
- mDragDampeningFactor = action.isActive()
- ? VERTICAL_GESTURE_DAMPING : VERTICAL_DISABLED_GESTURE_DAMPING;
- }
- }
-
- if (mHitTarget != null) {
- mHitTarget.abortCurrentGesture();
- }
- }
- }
-
- /**
- * To trigger an edge swipe, the user must start from the left or right edges of certain height
- * from the bottom then past the drag slope towards the center of the screen, followed by either
- * a timed trigger for fast swipes or distance if held on the screen longer.
- * For time, user must swipe up quickly before the Tap Timeout (typically 100ms) and for
- * distance, the user can drag back to cancel if the touch up has not past the threshold.
- * @param event Touch up event
- * @return whether or not edge swipe gesture occurs
- */
- private boolean canTriggerEdgeSwipe(MotionEvent event) {
- if (mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM) {
- return false;
- }
- int x = (int) event.getX();
- int y = (int) event.getY();
- int xDiff = Math.abs(x - mTouchDownX);
- int yDiff = Math.abs(y - mTouchDownY);
- final boolean exceededSwipeTouchSlop = xDiff > NavigationBarCompat.getQuickStepDragSlopPx()
- && xDiff > yDiff;
- if (exceededSwipeTouchSlop) {
- long timeDiff = event.getEventTime() - event.getDownTime();
- return xDiff > mGestureRegionThreshold || timeDiff < ViewConfiguration.getTapTimeout();
- }
- return false;
- }
-
- private boolean canPerformAnyAction() {
- for (NavigationGestureAction action: mGestureActions) {
- if (action != null && action.isEnabled()) {
- return true;
- }
- }
- return false;
- }
-
- private void sendTap(float x, float y) {
- long now = SystemClock.uptimeMillis();
- injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
- injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_UP, now, x, y, 0.0f);
- }
-
- private int getInputDeviceId(int inputSource) {
- int[] devIds = InputDevice.getDeviceIds();
- for (int devId : devIds) {
- InputDevice inputDev = InputDevice.getDevice(devId);
- if (inputDev.supportsSource(inputSource)) {
- return devId;
- }
- }
- return 0;
- }
-
- private void injectMotionEvent(int inputSource, int action, long when, float x, float y,
- float pressure) {
- final float defaultSize = 1.0f;
- final int defaultMetaState = 0;
- final float defaultPrecisionX = 1.0f;
- final float defaultPrecisionY = 1.0f;
- final int defaultEdgeFlags = 0;
- MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, defaultSize,
- defaultMetaState, defaultPrecisionX, defaultPrecisionY,
- getInputDeviceId(inputSource), defaultEdgeFlags);
- event.setSource(inputSource);
- InputManager.getInstance().injectInputEvent(event,
- InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
- }
-
- private boolean proxyMotionEvents(MotionEvent event) {
- event.transform(mTransformGlobalMatrix);
- InputEventDispatcher dispatcher = mOverviewEventSender.getInputEventDispatcher();
- if (dispatcher != null) {
- dispatcher.dispatch(event);
- }
-
- final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
- try {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
- }
- overviewProxy.onMotionEvent(event);
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- } finally {
- event.transform(mTransformLocalMatrix);
- }
- return false;
- }
-
- protected boolean isNavBarVertical() {
- return mNavBarPosition == NAV_BAR_LEFT || mNavBarPosition == NAV_BAR_RIGHT;
- }
-
- private static int convertDpToPixel(float dp) {
- return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
- }
-
- // TODO(112934365): Clean up following methods when cleaning up nav bar experiments
-
- static boolean getBoolGlobalSetting(Context context, String key) {
- return Settings.Global.getInt(context.getContentResolver(), key, 0) != 0;
- }
-
- static int getIntGlobalSetting(Context context, String key, int defaultValue) {
- return Settings.Global.getInt(context.getContentResolver(), key, defaultValue);
- }
-
- /**
- * @return whether to hide the back button.
- */
- public static boolean shouldHideBackButton(Context context) {
- return QuickStepContract.isGesturalMode(context);
- }
-
- /**
- * @return whether to hide the home button.
- */
- public static boolean shouldHideHomeButton(Context context) {
- return QuickStepContract.isGesturalMode(context);
- }
-
- /**
- * @return whether to show the home handle.
- */
- public static boolean showHomeHandle(Context context) {
- return QuickStepContract.isGesturalMode(context);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java
deleted file mode 100644
index 974de4b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
-
-import android.annotation.NonNull;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.recents.utilities.Utilities;
-
-/**
- * QuickSwitch action to send to launcher
- */
-public class QuickSwitchAction extends NavigationGestureAction {
- private static final String TAG = "QuickSwitchAction";
-
- protected final Rect mDragOverRect = new Rect();
-
- public QuickSwitchAction(@NonNull NavigationBarView navigationBar,
- @NonNull OverviewProxyService service) {
- super(navigationBar, service);
- }
-
- @Override
- public void setBarState(boolean changed, int navBarPos, boolean dragHorPositive,
- boolean dragVerPositive) {
- super.setBarState(changed, navBarPos, dragHorPositive, dragVerPositive);
- if (changed && isActive()) {
- // End quickscrub if the state changes mid-transition
- endQuickGesture(false /* animate */);
- }
- }
-
- @Override
- public boolean isEnabled() {
- return mNavigationBarView.isQuickScrubEnabled();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mDragOverRect.set(top, left, right, bottom);
- }
-
- @Override
- public boolean disableProxyEvents() {
- return true;
- }
-
- @Override
- protected void onGestureStart(MotionEvent event) {
- startQuickGesture(event);
- }
-
- @Override
- public void onGestureMove(int x, int y) {
- int dragLength, offset;
- if (isNavBarVertical()) {
- dragLength = mDragOverRect.height();
- offset = y - mDragOverRect.top;
- } else {
- offset = x - mDragOverRect.left;
- dragLength = mDragOverRect.width();
- }
- if (!mDragHorizontalPositive || !mDragVerticalPositive) {
- offset -= dragLength;
- }
- float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / dragLength, 0, 1);
- try {
- mProxySender.getProxy().onQuickScrubProgress(scrubFraction);
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Switch Progress:" + scrubFraction);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send progress of quick switch.", e);
- }
- }
-
- @Override
- protected void onGestureEnd() {
- endQuickGesture(true /* animate */);
- }
-
- protected void startQuickGesture(MotionEvent event) {
- // Disable slippery for quick scrub to not cancel outside the nav bar
- mNavigationBarView.updateSlippery();
-
- try {
- mProxySender.getProxy().onQuickScrubStart();
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub Start");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send start of quick scrub.", e);
- }
- mProxySender.notifyQuickScrubStarted();
- }
-
- protected void endQuickGesture(boolean animate) {
- try {
- mProxySender.getProxy().onQuickScrubEnd();
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub End");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send end of quick scrub.", e);
- }
- }
-}
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 ed66b4b..e5defae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2484,6 +2484,14 @@
options.setRotationAnimationHint(
WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
}
+ if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
+ // Settings Panel is implemented as activity(not a dialog), so
+ // underlying app is paused and may enter picture-in-picture mode
+ // as a result.
+ // So we need to disable picture-in-picture mode here
+ // if it is volume panel.
+ options.setDisallowEnterPictureInPictureWhileLaunching(true);
+ }
try {
result = ActivityTaskManager.getService().startActivityAsUser(
null, mContext.getBasePackageName(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3b32d95..92cd280 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -419,6 +419,7 @@
} else if (finishRunnable != null) {
finishRunnable.run();
}
+ mNotificationPanelView.blockExpansionForCurrentTouch();
}
/**
@@ -572,6 +573,10 @@
return mBouncer.isShowing();
}
+ public boolean isBouncerPartiallyVisible() {
+ return mBouncer.isPartiallyVisible();
+ }
+
public boolean isFullscreenBouncer() {
return mBouncer.isFullscreenBouncer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index 97be6ed..98cde2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -19,12 +19,12 @@
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.CastController.Callback;
-import java.util.Set;
+import java.util.List;
public interface CastController extends CallbackController<Callback>, Dumpable {
void setDiscovering(boolean request);
void setCurrentUserId(int currentUserId);
- Set<CastDevice> getCastDevices();
+ List<CastDevice> getCastDevices();
void startCasting(CastDevice device);
void stopCasting(CastDevice device);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index c7d337a..505dd16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -29,7 +29,6 @@
import android.os.Handler;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -40,8 +39,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.UUID;
import javax.inject.Inject;
@@ -150,8 +149,31 @@
}
@Override
- public Set<CastDevice> getCastDevices() {
- final ArraySet<CastDevice> devices = new ArraySet<CastDevice>();
+ public List<CastDevice> getCastDevices() {
+ final ArrayList<CastDevice> devices = new ArrayList<>();
+ synchronized(mRoutes) {
+ for (RouteInfo route : mRoutes.values()) {
+ final CastDevice device = new CastDevice();
+ device.id = route.getTag().toString();
+ final CharSequence name = route.getName(mContext);
+ device.name = name != null ? name.toString() : null;
+ final CharSequence description = route.getDescription();
+ device.description = description != null ? description.toString() : null;
+
+ int statusCode = route.getStatusCode();
+ if (statusCode == RouteInfo.STATUS_CONNECTING) {
+ device.state = CastDevice.STATE_CONNECTING;
+ } else if (route.isSelected() || statusCode == RouteInfo.STATUS_CONNECTED) {
+ device.state = CastDevice.STATE_CONNECTED;
+ } else {
+ device.state = CastDevice.STATE_DISCONNECTED;
+ }
+
+ device.tag = route;
+ devices.add(device);
+ }
+ }
+
synchronized (mProjectionLock) {
if (mProjection != null) {
final CastDevice device = new CastDevice();
@@ -161,24 +183,9 @@
device.state = CastDevice.STATE_CONNECTED;
device.tag = mProjection;
devices.add(device);
- return devices;
}
}
- synchronized(mRoutes) {
- for (RouteInfo route : mRoutes.values()) {
- final CastDevice device = new CastDevice();
- device.id = route.getTag().toString();
- final CharSequence name = route.getName(mContext);
- device.name = name != null ? name.toString() : null;
- final CharSequence description = route.getDescription();
- device.description = description != null ? description.toString() : null;
- device.state = route.isConnecting() ? CastDevice.STATE_CONNECTING
- : route.isSelected() ? CastDevice.STATE_CONNECTED
- : CastDevice.STATE_DISCONNECTED;
- device.tag = route;
- devices.add(device);
- }
- }
+
return devices;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 1e09063..c1950a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -146,7 +146,11 @@
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
results);
- RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
+ if (mEntry.editedSuggestionInfo == null) {
+ RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
+ } else {
+ RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
+ }
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 9343bf1..f726321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -403,7 +403,7 @@
final int N = mUsers.size();
for (int i = 0; i < N; ++i) {
UserRecord record = mUsers.get(i);
- if (record.info != null && record.info.supportsSwitchTo()) {
+ if (record.info != null && record.info.supportsSwitchToByUser()) {
count++;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
index 8ec66e4..45fc756 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
@@ -17,7 +17,10 @@
package com.android.systemui.volume;
import android.content.Context;
+import android.os.Handler;
import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
@@ -27,14 +30,34 @@
private static final int[] OPTED_OUT_STATE = new int[] { R.attr.optedOut };
+ private ConfirmedTapListener mConfirmedTapListener;
private boolean mComponentEnabled = false;
private boolean mOptedOut = false;
+ private GestureDetector mGestureDetector;
+ private GestureDetector.SimpleOnGestureListener mGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if (mConfirmedTapListener != null) {
+ mConfirmedTapListener.onConfirmedTap();
+ return true;
+ }
+ return false;
+ }
+ };
+
public CaptionsToggleImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mGestureDetector != null) mGestureDetector.onTouchEvent(event);
+ return super.onTouchEvent(event);
+ }
+
+ @Override
public int[] onCreateDrawableState(int extraSpace) {
int[] state = super.onCreateDrawableState(extraSpace + 1);
if (mOptedOut) {
@@ -64,4 +87,17 @@
boolean getOptedOut() {
return this.mOptedOut;
}
+
+ void setOnConfirmedTapListener(ConfirmedTapListener listener, Handler handler) {
+ mConfirmedTapListener = listener;
+
+ if (mGestureDetector == null) {
+ this.mGestureDetector = new GestureDetector(getContext(), mGestureListener, handler);
+ }
+ }
+
+ /** Listener for confirmed taps rather than normal on click listener. */
+ interface ConfirmedTapListener {
+ void onConfirmedTap();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 35a2450..2094b36 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -419,8 +419,9 @@
row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)});
}
row.dndIcon = row.view.findViewById(R.id.dnd_icon);
- row.slider = row.view.findViewById(R.id.volume_row_slider);
+ row.slider = row.view.findViewById(R.id.volume_row_slider);
row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
+
row.anim = null;
row.icon = row.view.findViewById(R.id.volume_row_icon);
@@ -515,11 +516,11 @@
private void initODICaptionsH() {
if (mODICaptionsIcon != null) {
- mODICaptionsIcon.setOnClickListener(v -> {
+ mODICaptionsIcon.setOnConfirmedTapListener(() -> {
onCaptionIconClicked();
Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_CLICK);
dismissH(DISMISS_REASON_ODI_CAPTIONS_CLICKED);
- });
+ }, mHandler);
}
mController.getCaptionsComponentState(false);
@@ -1393,6 +1394,7 @@
if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
mRow.userAttempt = SystemClock.uptimeMillis();
if (mRow.requestedLevel != userLevel) {
+ mController.setActiveStream(mRow.stream);
mController.setStreamVolume(mRow.stream, userLevel);
mRow.requestedLevel = userLevel;
Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 83ec33c..5412cde 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -71,8 +71,8 @@
# This appends a * to all classes and replace the space separators with commas.
jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.systemui.*
-LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := com.android.systemui.tests.*,$(jacoco_exclude)
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.systemui.*,com.android.keyguard.*
+LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
ifeq ($(EXCLUDE_SYSTEMUI_TESTS),)
include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 632b0c0..f01c0b4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -211,6 +211,19 @@
}
@Test
+ public void onPluginDisconnected_onDestroyView() {
+ // GIVEN a plugin is connected
+ ClockPlugin clockPlugin = mock(ClockPlugin.class);
+ when(clockPlugin.getView()).thenReturn(new TextClock(getContext()));
+ ClockManager.ClockChangedListener listener = mKeyguardClockSwitch.getClockChangedListener();
+ listener.onClockChanged(clockPlugin);
+ // WHEN the plugin is disconnected
+ listener.onClockChanged(null);
+ // THEN onDestroyView is called on the plugin
+ verify(clockPlugin).onDestroyView();
+ }
+
+ @Test
public void setTextColor_defaultClockSetTextColor() {
mKeyguardClockSwitch.setTextColor(Color.YELLOW);
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 9fa85d3..5e16721 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -18,9 +18,12 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -42,7 +45,6 @@
import androidx.test.filters.SmallTest;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationTestHelper;
@@ -96,9 +98,9 @@
private NotificationTestHelper mNotificationTestHelper;
private ExpandableNotificationRow mRow;
private ExpandableNotificationRow mRow2;
- private ExpandableNotificationRow mNoChannelRow;
private ExpandableNotificationRow mAutoExpandRow;
private ExpandableNotificationRow mSuppressNotifRow;
+ private ExpandableNotificationRow mNonBubbleNotifRow;
@Mock
private NotificationData mNotificationData;
@@ -107,9 +109,6 @@
@Mock
private BubbleController.BubbleExpandListener mBubbleExpandListener;
@Mock
- NotificationVisibility mNotificationVisibility;
-
- @Mock
private PendingIntent mDeleteIntent;
private BubbleData mBubbleData;
@@ -129,7 +128,7 @@
mNotificationTestHelper = new NotificationTestHelper(mContext);
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
- mNoChannelRow = mNotificationTestHelper.createBubble(mDeleteIntent);
+ mNonBubbleNotifRow = mNotificationTestHelper.createRow();
// Some bubbles want to auto expand
Notification.BubbleMetadata autoExpandMetadata =
@@ -146,7 +145,6 @@
// Return non-null notification data from the NEM
when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
- when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null);
mBubbleData = new BubbleData();
mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
@@ -391,8 +389,7 @@
mEntryListener.onPendingEntryAdded(mSuppressNotifRow.getEntry());
mBubbleController.updateBubble(mSuppressNotifRow.getEntry(), true /* updatePosition */);
- // Should be a bubble & should show in shade because we weren't forground
- assertTrue(mSuppressNotifRow.getEntry().isBubble());
+ // Should show in shade because we weren't forground
assertTrue(mSuppressNotifRow.getEntry().showInShadeWhenBubble());
// # of bubbles should change
@@ -428,8 +425,7 @@
mEntryListener.onPendingEntryAdded(mSuppressNotifRow.getEntry());
mBubbleController.updateBubble(mSuppressNotifRow.getEntry(), true /* updatePosition */);
- // Should be a bubble & should NOT show in shade because we were foreground
- assertTrue(mSuppressNotifRow.getEntry().isBubble());
+ // Should NOT show in shade because we were foreground
assertFalse(mSuppressNotifRow.getEntry().showInShadeWhenBubble());
// # of bubbles should change
@@ -444,8 +440,6 @@
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
- assertTrue(mRow.getEntry().isBubble());
-
// Simulate notification cancellation.
mEntryListener.onEntryRemoved(mRow.getEntry(), null /* notificationVisibility (unused) */,
false /* removedbyUser */);
@@ -454,18 +448,21 @@
}
@Test
- public void testMarkNewNotificationAsBubble() {
- mEntryListener.onPendingEntryAdded(mRow.getEntry());
- assertTrue(mRow.getEntry().isBubble());
- }
-
- @Test
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertTrue(mRow.getEntry().showInShadeWhenBubble());
}
@Test
+ public void testAddNotif_notBubble() {
+ mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry());
+
+ verify(mBubbleStateChangeListener, never()).onHasBubblesChanged(anyBoolean());
+ assertThat(mBubbleController.hasBubbles()).isFalse();
+ }
+
+ @Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_AGED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
new file mode 100644
index 0000000..801308f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleStackViewTest extends SysuiTestCase {
+ private BubbleStackView mStackView;
+ @Mock private Bubble mBubble;
+ @Mock private NotificationEntry mNotifEntry;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mStackView = new BubbleStackView(mContext, new BubbleData(), null);
+ mBubble.entry = mNotifEntry;
+ }
+
+ @Test
+ public void testAnimateInFlyoutForBubble() throws InterruptedException {
+ when(mNotifEntry.getUpdateMessage(any())).thenReturn("Test Flyout Message.");
+ mStackView.animateInFlyoutForBubble(mBubble);
+
+ // Wait for the fade in.
+ Thread.sleep(200);
+
+ // Flyout should be visible and showing our text.
+ assertEquals(1f, mStackView.findViewById(R.id.bubble_flyout).getAlpha(), .01f);
+ assertEquals("Test Flyout Message.",
+ ((TextView) mStackView.findViewById(R.id.bubble_flyout_text)).getText());
+
+ // Wait until it should have gone away.
+ Thread.sleep(BubbleStackView.FLYOUT_HIDE_AFTER + 200);
+
+ // Flyout should be gone.
+ assertEquals(View.GONE, mStackView.findViewById(R.id.bubble_flyout).getVisibility());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index 50fadef..910cee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -57,6 +57,7 @@
* direction is correct.
*/
@Test
+ @Ignore("Flaking")
public void testMoveFirstBubbleWithStackFollowing() throws InterruptedException {
mStackController.moveFirstBubbleWithStackFollowing(200, 100);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index ea8d4b2..818db87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -14,13 +14,19 @@
package com.android.systemui.qs.tiles;
+import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
+import android.media.projection.MediaProjectionInfo;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -33,6 +39,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -43,8 +50,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -93,7 +101,6 @@
verify(mNetworkController).observe(any(LifecycleOwner.class),
signalCallbackArgumentCaptor.capture());
mCallback = signalCallbackArgumentCaptor.getValue();
-
}
@Test
@@ -120,33 +127,125 @@
assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
}
- @Test
- public void testStateActive_wifiEnabledAndCasting() {
- CastController.CastDevice device = mock(CastController.CastDevice.class);
- device.state = CastController.CastDevice.STATE_CONNECTED;
- Set<CastController.CastDevice> devices = new HashSet<>();
- devices.add(device);
- when(mController.getCastDevices()).thenReturn(devices);
-
+ private void enableWifiAndProcessMessages() {
NetworkController.IconState qsIcon =
new NetworkController.IconState(true, 0, "");
mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
qsIcon, false,false, "",
false, "");
mTestableLooper.processAllMessages();
+ }
+ @Test
+ public void testStateActive_wifiEnabledAndCasting() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastController.CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
}
@Test
public void testStateInactive_wifiEnabledNotCasting() {
- NetworkController.IconState qsIcon =
- new NetworkController.IconState(true, 0, "");
- mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
- qsIcon, false,false, "",
- false, "");
+ enableWifiAndProcessMessages();
+ assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void testHandleClick_castDevicePresent() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ device.tag = mock(MediaRouter.RouteInfo.class);
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+ mCastTile.handleClick();
mTestableLooper.processAllMessages();
- assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ verify(mActivityStarter, times(1)).postQSRunnableDismissingKeyguard(any());
+ }
+
+ @Test
+ public void testHandleClick_projectionOnly() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ device.tag = mock(MediaProjectionInfo.class);
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+ mCastTile.handleClick();
+ mTestableLooper.processAllMessages();
+
+ verify(mController, times(1)).stopCasting(same(device));
+ }
+
+ @Test
+ public void testUpdateState_projectionOnly() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ device.tag = mock(MediaProjectionInfo.class);
+ device.name = "Test Projection Device";
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(device.name));
+ }
+
+ @Test
+ public void testUpdateState_castingAndProjection() {
+ CastController.CastDevice casting = new CastController.CastDevice();
+ casting.state = CastDevice.STATE_CONNECTED;
+ casting.tag = mock(RouteInfo.class);
+ casting.name = "Test Casting Device";
+
+ CastController.CastDevice projection = new CastController.CastDevice();
+ projection.state = CastDevice.STATE_CONNECTED;
+ projection.tag = mock(MediaProjectionInfo.class);
+ projection.name = "Test Projection Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(casting);
+ devices.add(projection);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ // Note here that the tile should be active, and should choose casting over projection.
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(casting.name));
+ }
+
+ @Test
+ public void testUpdateState_connectedAndConnecting() {
+ CastController.CastDevice connecting = new CastController.CastDevice();
+ connecting.state = CastDevice.STATE_CONNECTING;
+ connecting.tag = mock(RouteInfo.class);
+ connecting.name = "Test Casting Device";
+
+ CastController.CastDevice connected = new CastController.CastDevice();
+ connected.state = CastDevice.STATE_CONNECTED;
+ connected.tag = mock(RouteInfo.class);
+ connected.name = "Test Casting Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(connecting);
+ devices.add(connected);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ // Tile should be connected and always prefer the connected device.
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
}
}
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 7e089a6..8cc1571 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -154,9 +155,7 @@
*/
public ExpandableNotificationRow createBubble()
throws Exception {
- Notification n = createNotification(false /* isGroupSummary */,
- null /* groupKey */, makeBubbleMetadata(null));
- return generateRow(n, PKG, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
+ return createBubble(makeBubbleMetadata(null), PKG);
}
/**
@@ -166,21 +165,7 @@
*/
public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent)
throws Exception {
- Notification n = createNotification(false /* isGroupSummary */,
- null /* groupKey */, makeBubbleMetadata(deleteIntent));
- return generateRow(n, PKG, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
- }
-
- /**
- * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
- *
- * @param bubbleMetadata the {@link BubbleMetadata} to use
- */
- public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata)
- throws Exception {
- Notification n = createNotification(false /* isGroupSummary */,
- null /* groupKey */, bubbleMetadata);
- return generateRow(n, PKG, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
+ return createBubble(makeBubbleMetadata(deleteIntent), PKG);
}
/**
@@ -192,6 +177,7 @@
throws Exception {
Notification n = createNotification(false /* isGroupSummary */,
null /* groupKey */, bubbleMetadata);
+ n.flags |= FLAG_BUBBLE;
return generateRow(n, pkg, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
new file mode 100644
index 0000000..cca9f28
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationEntryTest extends SysuiTestCase {
+ @Mock
+ private StatusBarNotification mStatusBarNotification;
+ @Mock
+ private Notification mNotif;
+
+ private NotificationEntry mEntry;
+ private Bundle mExtras;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mStatusBarNotification.getKey()).thenReturn("key");
+ when(mStatusBarNotification.getNotification()).thenReturn(mNotif);
+
+ mExtras = new Bundle();
+ mNotif.extras = mExtras;
+
+ mEntry = new NotificationEntry(mStatusBarNotification);
+ }
+
+ @Test
+ public void testGetUpdateMessage_default() {
+ final String msg = "Hello there!";
+ doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
+ mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
+ assertEquals(msg, mEntry.getUpdateMessage(mContext));
+ }
+
+ @Test
+ public void testGetUpdateMessage_bigText() {
+ final String msg = "A big hello there!";
+ doReturn(Notification.BigTextStyle.class).when(mNotif).getNotificationStyle();
+ mExtras.putCharSequence(Notification.EXTRA_TEXT, "A small hello there.");
+ mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
+
+ // Should be big text, not the small text.
+ assertEquals(msg, mEntry.getUpdateMessage(mContext));
+ }
+
+ @Test
+ public void testGetUpdateMessage_media() {
+ doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
+
+ // Media notifs don't get update messages.
+ assertNull(mEntry.getUpdateMessage(mContext));
+ }
+
+ @Test
+ public void testGetUpdateMessage_inboxStyle() {
+ doReturn(Notification.InboxStyle.class).when(mNotif).getNotificationStyle();
+ mExtras.putCharSequenceArray(
+ Notification.EXTRA_TEXT_LINES,
+ new CharSequence[]{
+ "How do you feel about tests?",
+ "They're okay, I guess.",
+ "I hate when they're flaky.",
+ "Really? I prefer them that way."});
+
+ // Should be the last one only.
+ assertEquals("Really? I prefer them that way.", mEntry.getUpdateMessage(mContext));
+ }
+
+ @Test
+ public void testGetUpdateMessage_messagingStyle() {
+ doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
+ mExtras.putParcelableArray(
+ Notification.EXTRA_MESSAGES,
+ new Bundle[]{
+ new Notification.MessagingStyle.Message(
+ "Hello", 0, "Josh").toBundle(),
+ new Notification.MessagingStyle.Message(
+ "Oh, hello!", 0, "Mady").toBundle()});
+
+ // Should be the last one only.
+ assertEquals("Mady: Oh, hello!", mEntry.getUpdateMessage(mContext));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 1248cbb..67ad37b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations;
import java.util.Collections;
-import java.util.Set;
+import java.util.List;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -118,10 +118,10 @@
verify(mQsTileHost, never()).addTile("night");
}
- private static Set<CastDevice> buildFakeCastDevice(boolean isCasting) {
+ private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
CastDevice cd = new CastDevice();
cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED;
- return Collections.singleton(cd);
+ return Collections.singletonList(cd);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 3b56e45..6d6af47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
@@ -23,6 +25,8 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
@@ -32,6 +36,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -54,6 +59,8 @@
public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
+ private StatusBar mStatusBar;
+ @Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
@@ -62,12 +69,33 @@
@Mock
private KeyguardBottomAreaView mKeyguardBottomArea;
@Mock
+ private KeyguardBottomAreaView mQsFrame;
+ @Mock
+ private ViewGroup mBigClockContainer;
+ @Mock
+ private ScrimController mScrimController;
+ @Mock
+ private NotificationIconAreaController mNotificationAreaController;
+ @Mock
+ private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock
+ private NotificationShelf mNotificationShelf;
+ @Mock
+ private NotificationGroupManager mGroupManager;
+ @Mock
private KeyguardStatusBarView mKeyguardStatusBar;
+ @Mock
+ private HeadsUpTouchHelper.Callback mHeadsUpCallback;
+ @Mock
+ private PanelBar mPanelBar;
private NotificationPanelView mNotificationPanelView;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mNotificationStackScrollLayout.getHeight()).thenReturn(1000);
+ when(mNotificationStackScrollLayout.getHeadsUpCallback()).thenReturn(mHeadsUpCallback);
+ when(mHeadsUpCallback.getContext()).thenReturn(mContext);
mDependency.injectTestDependency(StatusBarStateController.class,
mStatusBarStateController);
mDependency.injectMockDependency(ShadeController.class);
@@ -80,6 +108,8 @@
new StatusBarStateControllerImpl());
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator);
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler);
+ mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
+ mNotificationPanelView.setBar(mPanelBar);
}
@Test
@@ -105,6 +135,37 @@
verify(mNotificationStackScrollLayout).setShowDarkShelf(eq(false));
}
+ @Test
+ public void testSetExpandedHeight() {
+ mNotificationPanelView.setExpandedHeight(200);
+ assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
+ }
+
+ @Test
+ public void testOnTouchEvent_expansionCanBeBlocked() {
+ mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
+ 0 /* metaState */));
+ mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
+ 0 /* metaState */));
+ assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
+ assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
+
+ mNotificationPanelView.blockExpansionForCurrentTouch();
+ mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */,
+ 0 /* metaState */));
+ // Expansion should not have changed because it was blocked
+ assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
+ assertThat(mNotificationPanelView.isTrackingBlocked()).isTrue();
+
+ mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+ 0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
+ 0 /* metaState */));
+ assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
+ }
+
private class TestableNotificationPanelView extends NotificationPanelView {
TestableNotificationPanelView(NotificationWakeUpCoordinator coordinator,
PulseExpansionHandler expansionHandler) {
@@ -116,6 +177,14 @@
mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar;
mKeyguardBottomArea = NotificationPanelViewTest.this.mKeyguardBottomArea;
+ mBigClockContainer = NotificationPanelViewTest.this.mBigClockContainer;
+ mQsFrame = NotificationPanelViewTest.this.mQsFrame;
+ initDependencies(NotificationPanelViewTest.this.mStatusBar,
+ NotificationPanelViewTest.this.mGroupManager,
+ NotificationPanelViewTest.this.mNotificationShelf,
+ NotificationPanelViewTest.this.mHeadsUpManager,
+ NotificationPanelViewTest.this.mNotificationAreaController,
+ NotificationPanelViewTest.this.mScrimController);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
deleted file mode 100644
index 7829830..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.MotionEvent;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.recents.IOverviewProxy;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.MockitoAnnotations;
-
-/** atest QuickStepControllerTest */
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QuickStepControllerTest extends SysuiTestCase {
- private static final int NAVBAR_WIDTH = 1000;
- private static final int NAVBAR_HEIGHT = 300;
-
- private QuickStepController mController;
- private NavigationBarView mNavigationBarView;
- private StatusBar mStatusBar;
- private OverviewProxyService mProxyService;
- private IOverviewProxy mProxy;
- private Resources mResources;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- final ButtonDispatcher backButton = mock(ButtonDispatcher.class);
- mResources = mock(Resources.class);
-
- mProxyService = mock(OverviewProxyService.class);
- mProxy = mock(IOverviewProxy.Stub.class);
- doReturn(mProxy).when(mProxyService).getProxy();
- doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
- mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
-
- mStatusBar = mock(StatusBar.class);
- doReturn(false).when(mStatusBar).isKeyguardShowing();
- mContext.putComponent(StatusBar.class, mStatusBar);
-
- mNavigationBarView = mock(NavigationBarView.class);
- doReturn(false).when(mNavigationBarView).inScreenPinning();
- doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
- doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
- doReturn(HIT_TARGET_NONE).when(mNavigationBarView).getDownHitTarget();
- doReturn(WINDOW_TARGET_BOTTOM).when(mNavigationBarView).getWindowTarget();
- doReturn(backButton).when(mNavigationBarView).getBackButton();
- doReturn(mResources).when(mNavigationBarView).getResources();
- doReturn(mContext).when(mNavigationBarView).getContext();
-
- mController = new QuickStepController(mContext);
- mController.setComponents(mNavigationBarView);
- mController.setBarState(false /* isRTL */, NAV_BAR_BOTTOM);
- }
-
- @Test
- public void testNoActionsNoGestures() throws Exception {
- MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
- assertFalse(mController.onInterceptTouchEvent(ev));
- verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
- assertNull(mController.getCurrentAction());
- }
-
- @Test
- public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
- doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
- mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
- assertFalse(mController.onInterceptTouchEvent(ev));
- verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
- assertNull(mController.getCurrentAction());
- }
-
- @Test
- public void testHasActionDetectGesturesTouchdown() throws Exception {
- MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
-
- // Add enabled gesture action
- NavigationGestureAction action = mockAction(true);
- mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- assertFalse(mController.onInterceptTouchEvent(ev));
- verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
- verify(action, times(1)).reset();
- verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
- verify(mProxy, times(1)).onMotionEvent(ev);
- assertNull(mController.getCurrentAction());
- }
-
- @Test
- public void testProxyDisconnectedNoGestures() throws Exception {
- MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
-
- // Add enabled gesture action
- mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- // Set the gesture on deadzone
- doReturn(null).when(mProxyService).getProxy();
-
- assertFalse(mController.onInterceptTouchEvent(ev));
- verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
- assertNull(mController.getCurrentAction());
- }
-
- @Test
- public void testNoActionsNoGesturesOverDeadzone() throws Exception {
- MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
-
- // Touched over deadzone
- doReturn(HIT_TARGET_DEAD_ZONE).when(mNavigationBarView).getDownHitTarget();
-
- assertTrue(mController.onInterceptTouchEvent(ev));
- verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
- assertNull(mController.getCurrentAction());
- }
-
- @Test
- public void testOnTouchIgnoredDownEventAfterOnIntercept() {
- mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
- assertFalse(touch(ev));
- verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
-
- // OnTouch event for down is ignored, so requestUnbufferedDispatch ran once from before
- assertFalse(mNavigationBarView.onTouchEvent(ev));
- verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
- }
-
- @Test
- public void testGesturesCallCorrectAction() throws Exception {
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
-
- NavigationGestureAction swipeUp = mockAction(true);
- NavigationGestureAction swipeDown = mockAction(true);
- NavigationGestureAction swipeLeft = mockAction(true);
- NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
- null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
-
- // Swipe Up
- assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
- // Swipe Down
- assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
- // Swipe Left
- assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1);
- // Swipe Right
- assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5);
- }
-
- @Test
- public void testGesturesCallCorrectActionLandscape() throws Exception {
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
-
- NavigationGestureAction swipeUp = mockAction(true);
- NavigationGestureAction swipeDown = mockAction(true);
- NavigationGestureAction swipeLeft = mockAction(true);
- NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
- null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
-
- // In landscape
- mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT);
-
- // Swipe Up
- assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
- // Swipe Down
- assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
- // Swipe Left
- assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
- // Swipe Right
- assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
- }
-
- @Test
- public void testGesturesCallCorrectActionSeascape() throws Exception {
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
-
- mController.setBarState(false /* isRTL */, NAV_BAR_LEFT);
- NavigationGestureAction swipeUp = mockAction(true);
- NavigationGestureAction swipeDown = mockAction(true);
- NavigationGestureAction swipeLeft = mockAction(true);
- NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
- null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
-
- // Swipe Up
- assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
- // Swipe Down
- assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
- // Swipe Left
- assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
- // Swipe Right
- assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
- }
-
- @Test
- public void testGesturesCallCorrectActionRTL() throws Exception {
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
- mController.setBarState(true /* isRTL */, NAV_BAR_BOTTOM);
-
- // The swipe gestures below are for LTR, so RTL in portrait will be swapped
- NavigationGestureAction swipeUp = mockAction(true);
- NavigationGestureAction swipeDown = mockAction(true);
- NavigationGestureAction swipeLeft = mockAction(true);
- NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
- null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
-
- // Swipe Up in RTL
- assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
- // Swipe Down in RTL
- assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
- // Swipe Left in RTL
- assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1);
- // Swipe Right in RTL
- assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0);
- }
-
- @Test
- public void testGesturesCallCorrectActionLandscapeRTL() throws Exception {
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
- mController.setBarState(true /* isRTL */, NAV_BAR_RIGHT);
-
- // The swipe gestures below are for LTR, so RTL in landscape will be swapped
- NavigationGestureAction swipeUp = mockAction(true);
- NavigationGestureAction swipeDown = mockAction(true);
- NavigationGestureAction swipeLeft = mockAction(true);
- NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
- null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
-
- // Swipe Up
- assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
- // Swipe Down
- assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
- // Swipe Left
- assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
- // Swipe Right
- assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
- }
-
- @Test
- public void testGesturesCallCorrectActionSeascapeRTL() throws Exception {
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
- mController.setBarState(true /* isRTL */, NAV_BAR_LEFT);
-
- // The swipe gestures below are for LTR, so RTL in seascape will be swapped
- NavigationGestureAction swipeUp = mockAction(true);
- NavigationGestureAction swipeDown = mockAction(true);
- NavigationGestureAction swipeLeft = mockAction(true);
- NavigationGestureAction swipeRight = mockAction(true);
- mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight,
- null /* leftEdgeSwipe */, null /* rightEdgeSwipe */);
-
- // Swipe Up
- assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1);
- // Swipe Down
- assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
- // Swipe Left
- assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
- // Swipe Right
- assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
- }
-
- @Test
- public void testActionPreventByPinnedState() throws Exception {
- // Screen is pinned
- doReturn(true).when(mNavigationBarView).inScreenPinning();
-
- // Add enabled gesture action
- NavigationGestureAction action = mockAction(true);
- mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- // Touch down to begin swipe
- MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
- assertFalse(touch(downEvent));
- verify(mProxy, never()).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
- verify(mProxy, never()).onMotionEvent(downEvent);
-
- // Move to start gesture, but pinned so it should not trigger action
- MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
- assertFalse(touch(moveEvent));
- assertNull(mController.getCurrentAction());
- verify(mNavigationBarView, times(1)).showPinningEscapeToast();
- verify(action, never()).onGestureStart(moveEvent);
- }
-
- @Test
- public void testActionPreventedNotificationsShown() throws Exception {
- NavigationGestureAction action = mockAction(true);
- doReturn(false).when(action).canRunWhenNotificationsShowing();
- mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- // Show the notifications
- doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
-
- // Swipe up
- assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
- assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
- assertNull(mController.getCurrentAction());
- assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
-
- // Hide the notifications
- doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
-
- // Swipe up
- assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
- assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
- assertEquals(action, mController.getCurrentAction());
- assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
- }
-
- @Test
- public void testActionCannotPerform() throws Exception {
- NavigationGestureAction action = mockAction(true);
- mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- // Cannot perform action
- doReturn(false).when(action).canPerformAction();
-
- // Swipe up
- assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
- assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
- assertNull(mController.getCurrentAction());
- assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
-
- // Cannot perform action
- doReturn(true).when(action).canPerformAction();
-
- // Swipe up
- assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
- assertTrue(touch(MotionEvent.ACTION_MOVE, 1, 1));
- assertEquals(action, mController.getCurrentAction());
- assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
- }
-
- @Test
- public void testQuickScrub() throws Exception {
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
- QuickScrubAction action = spy(new QuickScrubAction(mNavigationBarView, mProxyService));
- mController.setGestureActions(null /* swipeUpAction */, null /* swipeDownAction */,
- null /* swipeLeftAction */, action, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
- int x = NAVBAR_WIDTH / 2;
- int y = 20;
-
- // Set the layout and other padding to make sure the scrub fraction is calculated correctly
- action.onLayout(true, 0, 0, NAVBAR_WIDTH, NAVBAR_HEIGHT);
- doReturn(0).when(mNavigationBarView).getPaddingLeft();
- doReturn(0).when(mNavigationBarView).getPaddingRight();
- doReturn(0).when(mNavigationBarView).getPaddingStart();
- doReturn(0).when(mResources)
- .getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
-
- // Quickscrub disabled, so the action should be disabled
- doReturn(false).when(mNavigationBarView).isQuickScrubEnabled();
- assertFalse(action.isEnabled());
- doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
-
- // Touch down
- MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, x, y);
- assertFalse(touch(downEvent));
- assertNull(mController.getCurrentAction());
- verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
- verify(mProxy, times(1)).onMotionEvent(downEvent);
-
- // Move to start trigger action from gesture
- MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, x + 100, y);
- assertTrue(touch(moveEvent1));
- assertEquals(action, mController.getCurrentAction());
- verify(action, times(1)).onGestureStart(moveEvent1);
- verify(mProxy, times(1)).onQuickScrubStart();
- verify(mProxyService, times(1)).notifyQuickScrubStarted();
- verify(mNavigationBarView, times(1)).updateSlippery();
- verify(mProxy, never()).onMotionEvent(moveEvent1);
-
- // Move again for scrub
- float fraction = 3f / 4;
- x = (int) (NAVBAR_WIDTH * fraction);
- MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, x, y);
- assertTrue(touch(moveEvent2));
- assertEquals(action, mController.getCurrentAction());
- verify(action, times(1)).onGestureMove(x, y);
- verify(mProxy, times(1)).onQuickScrubProgress(fraction);
- verify(mProxy, never()).onMotionEvent(moveEvent2);
-
- // Action up
- MotionEvent upEvent = event(MotionEvent.ACTION_UP, 1, y);
- assertFalse(touch(upEvent));
- assertNull(mController.getCurrentAction());
- verify(action, times(1)).onGestureEnd();
- verify(mProxy, times(1)).onQuickScrubEnd();
- verify(mProxy, never()).onMotionEvent(upEvent);
- }
-
- @Test
- public void testQuickStep() throws Exception {
- QuickStepAction action = new QuickStepAction(mNavigationBarView, mProxyService);
- mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- // Notifications are up, should prevent quickstep
- doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
-
- // Swipe up
- assertFalse(touch(MotionEvent.ACTION_DOWN, 1, 100));
- assertNull(mController.getCurrentAction());
- assertFalse(touch(MotionEvent.ACTION_MOVE, 1, 1));
- assertNull(mController.getCurrentAction());
- assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
- doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed();
-
- // Quickstep disabled, so the action should be disabled
- doReturn(false).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
- assertFalse(action.isEnabled());
- doReturn(true).when(mNavigationBarView).isQuickStepSwipeUpEnabled();
-
- // Swipe up should call proxy events
- MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
- assertFalse(touch(downEvent));
- assertNull(mController.getCurrentAction());
- verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
- verify(mProxy, times(1)).onMotionEvent(downEvent);
-
- MotionEvent moveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
- assertTrue(touch(moveEvent));
- assertEquals(action, mController.getCurrentAction());
- verify(mProxy, times(1)).onQuickStep(moveEvent);
- verify(mProxyService, times(1)).notifyQuickStepStarted();
- }
-
- @Test
- public void testLongPressPreventDetection() throws Exception {
- NavigationGestureAction action = mockAction(true);
- mController.setGestureActions(action, null /* swipeDownAction */,
- null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
- null /* rightEdgeSwipe */);
-
- // Start the drag up
- assertFalse(touch(MotionEvent.ACTION_DOWN, 100, 1));
- assertNull(mController.getCurrentAction());
-
- // Long press something on the navigation bar such as Home button
- mNavigationBarView.onNavigationButtonLongPress(mock(View.class));
-
- // Swipe right will not start any gestures
- MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, 1, 1);
- assertFalse(touch(motionMoveEvent));
- assertNull(mController.getCurrentAction());
- verify(action, never()).startGesture(motionMoveEvent);
-
- // Touch up
- assertFalse(touch(MotionEvent.ACTION_UP, 1, 1));
- verify(action, never()).endGesture();
- }
-
- @Test
- public void testHitTargetDragged() throws Exception {
- ButtonDispatcher button = mock(ButtonDispatcher.class);
- FakeLocationView buttonView = spy(new FakeLocationView(mContext, NAVBAR_WIDTH / 2,
- NAVBAR_HEIGHT / 2));
- doReturn(buttonView).when(button).getCurrentView();
-
- NavigationGestureAction action = mockAction(true);
- mController.setGestureActions(action, action, action, action, action, action);
-
- // Setup getting the hit target
- doReturn(HIT_TARGET_HOME).when(action).requiresTouchDownHitTarget();
- doReturn(true).when(action).allowHitTargetToMoveOverDrag();
- doReturn(HIT_TARGET_HOME).when(mNavigationBarView).getDownHitTarget();
- doReturn(button).when(mNavigationBarView).getHomeButton();
- doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
- doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
-
- // Portrait
- assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_BOTTOM);
-
- // Portrait RTL
- assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_BOTTOM);
-
- // Landscape
- assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_RIGHT);
-
- // Landscape RTL
- assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_RIGHT);
-
- // Seascape
- assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_LEFT);
-
- // Seascape RTL
- assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT);
- }
-
- private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL,
- int navPos) {
- mController.setBarState(isRTL, navPos);
-
- // Swipe up
- assertGestureDragsHitTarget(buttonView, 10 /* x1 */, 200 /* y1 */, 0 /* x2 */, 0 /* y2 */,
- 0 /* dx */, -1 /* dy */);
- // Swipe left
- assertGestureDragsHitTarget(buttonView, 200 /* x1 */, 10 /* y1 */, 0 /* x2 */, 0 /* y2 */,
- -1 /* dx */, 0 /* dy */);
- // Swipe right
- assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 200 /* x2 */, 10 /* y2 */,
- 1 /* dx */, 0 /* dy */);
- // Swipe down
- assertGestureDragsHitTarget(buttonView, 0 /* x1 */, 0 /* y1 */, 10 /* x2 */, 200 /* y2 */,
- 0 /* dx */, 1 /* dy */);
- }
-
- /**
- * Asserts the gesture actually moves the hit target
- * @param buttonView button to check if moved, use Mockito.spy on a real object
- * @param x1 start x
- * @param x2 start y
- * @param y1 end x
- * @param y2 end y
- * @param dx diff in x, if not 0, its sign determines direction, value does not matter
- * @param dy diff in y, if not 0, its sign determines direction, value does not matter
- */
- private void assertGestureDragsHitTarget(View buttonView, int x1, int y1, int x2, int y2,
- int dx, int dy) {
- ArgumentCaptor<Float> captor = ArgumentCaptor.forClass(Float.class);
- assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
- assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
-
- // Verify positions of the button drag
- if (dx == 0) {
- verify(buttonView, never()).setTranslationX(anyFloat());
- } else {
- verify(buttonView).setTranslationX(captor.capture());
- if (dx < 0) {
- assertTrue("Button should have moved left", (float) captor.getValue() < 0);
- } else {
- assertTrue("Button should have moved right", (float) captor.getValue() > 0);
- }
- }
- if (dy == 0) {
- verify(buttonView, never()).setTranslationY(anyFloat());
- } else {
- verify(buttonView).setTranslationY(captor.capture());
- if (dy < 0) {
- assertTrue("Button should have moved up", (float) captor.getValue() < 0);
- } else {
- assertTrue("Button should have moved down", (float) captor.getValue() > 0);
- }
- }
-
- // Touch up
- assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
- verify(buttonView, times(1)).animate();
-
- // Reset button state
- reset(buttonView);
- }
-
- private MotionEvent event(int action, float x, float y) {
- final MotionEvent event = mock(MotionEvent.class);
- doReturn(x).when(event).getX();
- doReturn(y).when(event).getY();
- doReturn(action & MotionEvent.ACTION_MASK).when(event).getActionMasked();
- doReturn(action).when(event).getAction();
- return event;
- }
-
- private boolean touch(int action, float x, float y) {
- return touch(event(action, x, y));
- }
-
- private boolean touch(MotionEvent event) {
- return mController.onInterceptTouchEvent(event);
- }
-
- private NavigationGestureAction mockAction(boolean enabled) {
- final NavigationGestureAction action = mock(NavigationGestureAction.class);
- doReturn(enabled).when(action).isEnabled();
- doReturn(HIT_TARGET_NONE).when(action).requiresTouchDownHitTarget();
- doReturn(true).when(action).canPerformAction();
- return action;
- }
-
- private void assertGestureTriggersAction(NavigationGestureAction action, int x1, int y1,
- int x2, int y2) {
- // Start the drag
- assertFalse(touch(MotionEvent.ACTION_DOWN, x1, y1));
- assertNull(mController.getCurrentAction());
-
- // Swipe
- MotionEvent motionMoveEvent = event(MotionEvent.ACTION_MOVE, x2, y2);
- assertTrue(touch(motionMoveEvent));
- assertEquals(action, mController.getCurrentAction());
- verify(action, times(1)).startGesture(motionMoveEvent);
-
- // Move again
- assertTrue(touch(MotionEvent.ACTION_MOVE, x2, y2));
- verify(action, times(1)).onGestureMove(x2, y2);
-
- // Touch up
- assertFalse(touch(MotionEvent.ACTION_UP, x2, y2));
- assertNull(mController.getCurrentAction());
- verify(action, times(1)).endGesture();
- }
-
- static class FakeLocationView extends View {
- private final int mX;
- private final int mY;
-
- public FakeLocationView(Context context, int x, int y) {
- super(context);
- mX = x;
- mY = y;
- }
-
- @Override
- public void getLocationInWindow(int[] outLocation) {
- outLocation[0] = mX;
- outLocation[1] = mY;
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 41e82cb..51fb47b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -152,7 +152,6 @@
bubbleSbn.getNotification().contentIntent = mContentIntent;
bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
// Do what BubbleController's NotificationEntryListener#onPendingEntryAdded does:
- mBubbleNotificationRow.getEntry().setIsBubble(true);
mBubbleNotificationRow.getEntry().setShowInShadeWhenBubble(true);
mActiveNotifications = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index cd9069a..3edfb56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -95,7 +95,7 @@
triggerConstantsOnChange();
assertEquals(false, mConstants.requiresTargetingP());
- overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "");
+ overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, null);
triggerConstantsOnChange();
assertEquals(true, mConstants.requiresTargetingP());
}
@@ -223,21 +223,21 @@
private void resetAllDeviceConfigFlags() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_ENABLED, "", false /* makeDefault */);
+ SystemUiDeviceConfigFlags.SSIN_ENABLED, null, false /* makeDefault */);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "", false /* makeDefault */);
+ SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, null, false /* makeDefault */);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, "",
+ SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, null,
false /* makeDefault */);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "",
+ SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, null,
false /* makeDefault */);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "", false /* makeDefault */);
+ SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, null, false /* makeDefault */);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, "",
+ SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, null,
false /* makeDefault */);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, "", false /* makeDefault */);
+ SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, null, false /* makeDefault */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
index 51149ab..f6b24da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -19,7 +19,8 @@
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.Callback;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
public class FakeCastController extends BaseLeakChecker<Callback> implements CastController {
public FakeCastController(LeakCheck test) {
@@ -37,8 +38,8 @@
}
@Override
- public Set<CastDevice> getCastDevices() {
- return null;
+ public List<CastDevice> getCastDevices() {
+ return new ArrayList<>();
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3c69bb7..6e2c228 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4126,6 +4126,13 @@
return mBindingServices;
}
+ /**
+ * Returns enabled service list.
+ */
+ public Set<ComponentName> getEnabledServicesLocked() {
+ return mEnabledServices;
+ }
+
public int getSoftKeyboardShowMode() {
return mSoftKeyboardShowMode;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 3bd6220..b66caa5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -123,12 +123,12 @@
synchronized (mLock) {
UserState userState = mUserStateWeakReference.get();
if (userState == null) return;
- if (userState.mEnabledServices.remove(mComponentName)) {
+ if (userState.getEnabledServicesLocked().remove(mComponentName)) {
final long identity = Binder.clearCallingIdentity();
try {
mSystemSupport.persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userState.mEnabledServices, userState.mUserId);
+ userState.getEnabledServicesLocked(), userState.mUserId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -183,6 +183,14 @@
mWasConnectedAndDied = false;
serviceInterface = mServiceInterface;
}
+ // There's a chance that service is removed from enabled_accessibility_services setting
+ // key, but skip unbinding because of it's in binding state. Unbinds it if it's
+ // not in enabled service list.
+ if (serviceInterface != null
+ && !userState.getEnabledServicesLocked().contains(mComponentName)) {
+ mSystemSupport.onClientChangeLocked(false);
+ return;
+ }
}
if (serviceInterface == null) {
binderDied();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 7106664..79afe8e 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -31,11 +31,14 @@
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
+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.os.Binder;
+import android.os.FileUtils;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -49,6 +52,7 @@
import com.android.server.SystemConfig;
import com.android.server.SystemService;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
@@ -86,6 +90,18 @@
private Set<ComponentName> mTransportWhitelist;
+ private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId > 0) { // for only non system users
+ onRemovedNonSystemUser(userId);
+ }
+ }
+ }
+ };
+
/** Instantiate a new instance of {@link BackupManagerService}. */
public BackupManagerService(
Context context, Trampoline trampoline, HandlerThread backupThread) {
@@ -99,6 +115,23 @@
if (mTransportWhitelist == null) {
mTransportWhitelist = Collections.emptySet();
}
+
+ mContext.registerReceiver(mUserRemovedReceiver,
+ new IntentFilter(Intent.ACTION_USER_REMOVED));
+ }
+
+ /**
+ * Remove backup state for non system {@code userId} when the user is removed from the device.
+ * For non system users, backup state is stored in both the user's own dir and the system dir.
+ * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
+ * the part of the user backup state which is in the system dir also gets removed.
+ */
+ private void onRemovedNonSystemUser(int userId) {
+ Slog.i(TAG, "Removing state for non system user " + userId);
+ File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
+ if (!FileUtils.deleteContentsAndDir(dir)) {
+ Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
+ }
}
/**
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index bb0aba0..a9b292b3 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -47,7 +47,6 @@
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;
@@ -95,7 +94,7 @@
* 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";
+ private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";
// Product-level suppression of backup/restore.
private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
@@ -143,8 +142,7 @@
/** Stored in the system user's directory and the file is indexed by the user it refers to. */
protected File getRememberActivatedFileForNonSystemUser(int userId) {
- return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir(
- REMEMBER_ACTIVATED_FILENAME_PREFIX, userId));
+ return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
}
/** Stored in the system user's directory and the file is indexed by the user it refers to. */
@@ -336,8 +334,13 @@
// 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);
+ try {
+ File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
+ createFile(rememberFile);
+ RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service activity", e);
+ }
}
if (mGlobalDisable) {
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
index 4638ac6..8e60542 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -49,8 +49,13 @@
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);
+ /** A user specific dir within the system user's directory. */
+ static File getStateDirInSystemDir(int userId) {
+ return new File(getBaseStateDir(UserHandle.USER_SYSTEM), "" + userId);
+ }
+
+ /** Stored in a user specific dir within the system user's directory. */
+ static File getStateFileInSystemDir(String filename, int userId) {
+ return new File(getStateDirInSystemDir(userId), filename);
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a7404bc..a3e7d36 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -164,6 +164,20 @@
}
@Override
+ public void onUnlockUser(int userHandle) {
+ Set<Association> associations = readAllAssociations(userHandle);
+ Set<String> companionAppPackages = new HashSet<>();
+ for (Association association : associations) {
+ companionAppPackages.add(association.companionAppPackage);
+ }
+ ActivityTaskManagerInternal atmInternal = LocalServices.getService(
+ ActivityTaskManagerInternal.class);
+ if (atmInternal != null) {
+ atmInternal.setCompanionAppPackages(userHandle, companionAppPackages);
+ }
+ }
+
+ @Override
public void binderDied() {
Handler.getMain().post(this::cleanup);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8ad23b0..6405254 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -951,7 +951,7 @@
mTethering = makeTethering();
- mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
+ mPermissionMonitor = new PermissionMonitor(mContext, mNMS, mNetd);
// Set up the listener for user state for creating user VPNs.
// Should run on mHandler to avoid any races.
@@ -6382,6 +6382,11 @@
Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
}
+ // NetworkCapabilities need to be set before sending the private DNS config to
+ // NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required.
+ synchronized (networkAgent) {
+ networkAgent.setNetworkCapabilities(networkAgent.networkCapabilities);
+ }
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
null);
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index f5bd11c..99bbcf8 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -70,25 +70,24 @@
checkPermission();
if (!"running".equals(SystemProperties.get("init.svc.gsid"))) {
SystemProperties.set("ctl.start", "gsid");
- }
- for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) {
- try {
- Thread.sleep(sleepMs);
- } catch (InterruptedException e) {
- Slog.e(TAG, "Interrupted when waiting for GSID");
- break;
- }
- if ("running".equals(SystemProperties.get("init.svc.gsid"))) {
- synchronized (this) {
- if (mGsiService == null) {
- mGsiService = connect(this);
- }
- return mGsiService;
+ for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) {
+ try {
+ Thread.sleep(sleepMs);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Interrupted when waiting for GSID");
+ break;
+ }
+ if ("running".equals(SystemProperties.get("init.svc.gsid"))) {
+ break;
}
}
}
- Slog.e(TAG, "Unable to start gsid");
- return null;
+ synchronized (this) {
+ if (mGsiService == null) {
+ mGsiService = connect(this);
+ }
+ return mGsiService;
+ }
}
private void checkPermission() {
@@ -125,19 +124,24 @@
}
@Override
+ public boolean isEnabled() throws RemoteException {
+ return getGsiService().isGsiEnabled();
+ }
+
+ @Override
public boolean remove() throws RemoteException {
return getGsiService().removeGsiInstall();
}
@Override
- public boolean toggle() throws RemoteException {
+ public boolean setEnable(boolean enable) throws RemoteException {
IGsiService gsiService = getGsiService();
- if (gsiService.isGsiRunning()) {
- return gsiService.disableGsiInstall();
- } else {
+ if (enable) {
final int status = gsiService.getGsiBootStatus();
final boolean singleBoot = (status == IGsiService.BOOT_STATUS_SINGLE_BOOT);
return gsiService.setGsiBootable(singleBoot) == 0;
+ } else {
+ return gsiService.disableGsiInstall();
}
}
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index a5a515f..e3dc3b7 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -382,7 +382,13 @@
/**
* Runs the given function synchronously if currently connected, and returns the default value
* if not currently connected or if any exception is thrown.
+ *
+ * @deprecated Using this function is an indication that your AIDL API is broken. Calls from
+ * system server to outside MUST be one-way, and so cannot return any result, and this
+ * method should not be needed or used. Use a separate callback interface to allow outside
+ * components to return results back to the system server.
*/
+ @Deprecated
public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) {
try {
return runOnHandlerBlocking(() -> {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4598d3e..36fa3c6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -929,13 +929,20 @@
private void initIfBootedAndConnected() {
Slog.d(TAG, "Thinking about init, mBootCompleted=" + mBootCompleted
+ ", mDaemonConnected=" + mDaemonConnected);
- if (mBootCompleted && mDaemonConnected
- && !StorageManager.isFileEncryptedNativeOnly()) {
- // When booting a device without native support, make sure that our
- // user directories are locked or unlocked based on the current
- // emulation status.
- final boolean initLocked = StorageManager.isFileEncryptedEmulatedOnly();
- Slog.d(TAG, "Setting up emulation state, initlocked=" + initLocked);
+ if (mBootCompleted && mDaemonConnected) {
+ // Tell vold to lock or unlock the user directories based on the
+ // current file-based encryption status.
+ final boolean initLocked;
+ if (StorageManager.isFileEncryptedNativeOrEmulated()) {
+ // For native FBE this is a no-op after reboot, but this is
+ // still needed in case of framework restarts.
+ Slog.d(TAG, "FBE is enabled; ensuring all user directories are locked.");
+ initLocked = true;
+ } else {
+ // This is in case FBE emulation was turned off.
+ Slog.d(TAG, "FBE is disabled; ensuring the FBE emulation state is cleared.");
+ initLocked = false;
+ }
final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
for (UserInfo user : users) {
try {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 261ed4c..f0982d3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1344,7 +1344,8 @@
if (!r.isForeground) {
final ServiceMap smap = getServiceMapLocked(r.userId);
if (smap != null) {
- ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);
+ ActiveForegroundApp active = smap.mActiveForegroundApps
+ .get(r.packageName);
if (active == null) {
active = new ActiveForegroundApp();
active.mPackageName = r.packageName;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e0e4d61..a5932cc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15335,18 +15335,20 @@
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
+
final long origId = Binder.clearCallingIdentity();
- int res = broadcastIntentLocked(callerApp,
- callerApp != null ? callerApp.info.packageName : null,
- intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
- requiredPermissions, appOp, bOptions, serialized, sticky,
- callingPid, callingUid, callingUid, callingPid, userId);
- Binder.restoreCallingIdentity(origId);
- return res;
+ try {
+ return broadcastIntentLocked(callerApp,
+ callerApp != null ? callerApp.info.packageName : null,
+ intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
+ requiredPermissions, appOp, bOptions, serialized, sticky,
+ callingPid, callingUid, callingUid, callingPid, userId);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
}
-
int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
@@ -15358,13 +15360,15 @@
final long origId = Binder.clearCallingIdentity();
String[] requiredPermissions = requiredPermission == null ? null
: new String[] {requiredPermission};
- int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
- resultTo, resultCode, resultData, resultExtras,
- requiredPermissions, OP_NONE, bOptions, serialized,
- sticky, -1, uid, realCallingUid, realCallingPid, userId,
- allowBackgroundActivityStarts);
- Binder.restoreCallingIdentity(origId);
- return res;
+ try {
+ return broadcastIntentLocked(null, packageName, intent, resolvedType,
+ resultTo, resultCode, resultData, resultExtras,
+ requiredPermissions, OP_NONE, bOptions, serialized,
+ sticky, -1, uid, realCallingUid, realCallingPid, userId,
+ allowBackgroundActivityStarts);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8a462da..d1379b6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
@@ -427,8 +428,12 @@
if (intent.getComponent() != null) {
packageName = intent.getComponent().getPackageName();
} else {
+ // queryIntentActivities does not convert user id, so we convert it here first
+ int userIdForQuery = mInternal.mUserController.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(), mUserId, false,
+ ALLOW_NON_FULL, "ActivityManagerShellCommand", null);
List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
- mUserId).getList();
+ userIdForQuery).getList();
if (activities == null || activities.size() <= 0) {
getErrPrintWriter().println("Error: Intent does not match any activities: "
+ intent);
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index f58fb95..ac10026 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -485,9 +485,10 @@
long zramFreeKbAfter = Debug.getZramFreeKb();
EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ rssAfter[0] - rssBefore[0], rssAfter[1] - rssBefore[1],
+ rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time,
lastCompactAction, lastCompactTime, msg.arg1, msg.arg2,
- zramFreeKbBefore, zramFreeKbAfter);
+ zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore);
// Note that as above not taking mPhenoTypeFlagLock here to avoid locking
// on every single compaction for a flag that will seldom change and the
// impact of reading the wrong value here is low.
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index a1c941e..fe95542 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -109,6 +109,14 @@
clientPackageName = _clientPackageName;
}
+ public boolean hasFlag(final int flag) {
+ return (flags & flag) != 0;
+ }
+
+ public boolean notHasFlag(final int flag) {
+ return (flags & flag) == 0;
+ }
+
public void startAssociationIfNeeded() {
// If we don't already have an active association, create one... but only if this
// is an association between two different processes.
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 4b12e43..e65a4e5 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -138,4 +138,4 @@
30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
# The task is being compacted
-30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2),(BeforeZRAMFree|2|2),(AfterZRAMFree|2|2)
+30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(DeltaRssTotal|2|2),(DeltaRssFile|2|2),(DeltaRssAnon|2|2),(DeltaRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2),(BeforeZRAMFree|2|2),(DeltaZRAMFree|2|2)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 924e331..5d47c9d 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -16,10 +16,19 @@
package com.android.server.am;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
import static android.os.Process.SCHED_OTHER;
import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
import static android.os.Process.THREAD_GROUP_DEFAULT;
@@ -413,7 +422,7 @@
app.kill("cached #" + numCached, true);
}
break;
- case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+ case PROCESS_STATE_CACHED_EMPTY:
if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
&& app.lastActivityTime < oldTime) {
app.kill("empty for "
@@ -718,7 +727,7 @@
if (app.thread == null) {
app.adjSeq = mAdjSeq;
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
- app.setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ app.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
app.curAdj = ProcessList.CACHED_APP_MAX_ADJ;
app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
app.completedAdjSeq = app.adjSeq;
@@ -773,7 +782,7 @@
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
} else {
// screen off, restrict UI scheduling
- app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ app.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
}
}
@@ -797,7 +806,7 @@
boolean foregroundActivities = false;
mTmpBroadcastQueue.clear();
- if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
+ if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == TOP_APP) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
@@ -820,7 +829,7 @@
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.adjType = "instrumentation";
- procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ procState = PROCESS_STATE_FOREGROUND_SERVICE;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
}
@@ -844,7 +853,7 @@
schedGroup = app.execServicesFg ?
ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "exec-service";
- procState = ActivityManager.PROCESS_STATE_SERVICE;
+ procState = PROCESS_STATE_SERVICE;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
}
@@ -864,7 +873,7 @@
// At this point we don't actually know the adjustment. Use the cached adj
// value that the caller wants us to.
adj = cachedAdj;
- procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ procState = PROCESS_STATE_CACHED_EMPTY;
app.cached = true;
app.empty = true;
app.adjType = "cch-empty";
@@ -899,23 +908,28 @@
}
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
- || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION) {
+ || procState > PROCESS_STATE_FOREGROUND_SERVICE_LOCATION) {
if (app.hasForegroundServices()) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- procState = app.hasLocationForegroundServices()
- ? ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION
- : ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ if (app.hasLocationForegroundServices()) {
+ procState = PROCESS_STATE_FOREGROUND_SERVICE_LOCATION;
+ app.adjType = "fg-service-location";
+
+ } else {
+ procState = PROCESS_STATE_FOREGROUND_SERVICE;
+ app.adjType = "fg-service";
+ }
app.cached = false;
- app.adjType = "fg-service";
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app);
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + app.adjType + ": "
+ + app + " ");
}
} else if (app.hasOverlayUi()) {
// The process is display an overlay UI.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
app.cached = false;
app.adjType = "has-overlay-ui";
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -930,7 +944,7 @@
// services so that it can finish performing any persistence/processing of in-memory state.
if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
&& (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
- || app.setProcState <= ActivityManager.PROCESS_STATE_TOP)) {
+ || app.setProcState <= PROCESS_STATE_TOP)) {
adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
app.adjType = "fg-service-act";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -939,13 +953,13 @@
}
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
- || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ || procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
if (app.forcingToImportant != null) {
// This is currently used for toasts... they are not interactive, and
// we don't want them to cause the app to become fully foreground (and
// thus out of background check), so we yes the best background level we can.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
app.cached = false;
app.adjType = "force-imp";
app.adjSource = app.forcingToImportant;
@@ -1041,8 +1055,8 @@
if (adj > ProcessList.BACKUP_APP_ADJ) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
adj = ProcessList.BACKUP_APP_ADJ;
- if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
- procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
}
app.adjType = "backup";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -1059,21 +1073,16 @@
}
}
- boolean mayBeTop = false;
- String mayBeTopType = null;
- Object mayBeTopSource = null;
- Object mayBeTopTarget = null;
-
for (int is = app.services.size() - 1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- || procState > ActivityManager.PROCESS_STATE_TOP);
+ || procState > PROCESS_STATE_TOP);
is--) {
ServiceRecord s = app.services.valueAt(is);
if (s.startRequested) {
app.hasStartedServices = true;
- if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
- procState = ActivityManager.PROCESS_STATE_SERVICE;
+ if (procState > PROCESS_STATE_SERVICE) {
+ procState = PROCESS_STATE_SERVICE;
app.adjType = "started-services";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
@@ -1116,13 +1125,13 @@
for (int conni = serviceConnections.size() - 1;
conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- || procState > ActivityManager.PROCESS_STATE_TOP);
+ || procState > PROCESS_STATE_TOP);
conni--) {
ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
for (int i = 0;
i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- || procState > ActivityManager.PROCESS_STATE_TOP);
+ || procState > PROCESS_STATE_TOP);
i++) {
// XXX should compute this based on the max of
// all connected clients.
@@ -1148,7 +1157,7 @@
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
// doesn't propagate except under certain conditions.
- clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ clientProcState = PROCESS_STATE_CACHED_EMPTY;
}
String adjType = null;
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
@@ -1219,6 +1228,7 @@
newAdj = clientAdj;
} else {
if (adj > ProcessList.VISIBLE_APP_ADJ) {
+ // TODO: Is this too limiting for apps bound from TOP?
newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
} else {
newAdj = adj;
@@ -1247,55 +1257,50 @@
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
- if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
- if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
- // Special handling of clients who are in the top state.
- // We *may* want to consider this process to be in the
- // top state as well, but only if there is not another
- // reason for it to be running. Being on the top is a
- // special state, meaning you are specifically running
- // for the current top app. If the process is already
- // running in the background for some other reason, it
- // is more important to continue considering it to be
- // in the background state.
- mayBeTop = true;
- mayBeTopType = "service";
- mayBeTopSource = cr.binding.client;
- mayBeTopTarget = s.instanceName;
- clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ if (clientProcState < PROCESS_STATE_TOP) {
+ // Special handling for above-top states (persistent
+ // processes). These should not bring the current process
+ // into the top state, since they are not on top. Instead
+ // give them the best bound state after that.
+ if ((cr.flags & Context.BIND_FOREGROUND_SERVICE) != 0) {
+ clientProcState =
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ } else if (mService.mWakefulness
+ == PowerManagerInternal.WAKEFULNESS_AWAKE
+ && (cr.flags & Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
+ != 0) {
+ clientProcState =
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
} else {
- // Special handling for above-top states (persistent
- // processes). These should not bring the current process
- // into the top state, since they are not on top. Instead
- // give them the best state after that.
- if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
- clientProcState =
- ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- } else if (mService.mWakefulness
- == PowerManagerInternal.WAKEFULNESS_AWAKE &&
- (cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
- != 0) {
- clientProcState =
- ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- } else {
- clientProcState =
- ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
- }
+ clientProcState =
+ PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
+ } else if (clientProcState == PROCESS_STATE_TOP) {
+ if (cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
+ // Go at most to BOUND_TOP, unless requested to elevate
+ // to client's state.
+ clientProcState = PROCESS_STATE_BOUND_TOP;
+ }
+ } else if (clientProcState
+ <= PROCESS_STATE_FOREGROUND_SERVICE) {
+ if (cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
+ clientProcState = PROCESS_STATE_FOREGROUND_SERVICE;
}
}
} else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
if (clientProcState <
- ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ PROCESS_STATE_TRANSIENT_BACKGROUND) {
clientProcState =
- ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ PROCESS_STATE_TRANSIENT_BACKGROUND;
}
} else {
if (clientProcState <
- ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ PROCESS_STATE_IMPORTANT_BACKGROUND) {
clientProcState =
- ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ PROCESS_STATE_IMPORTANT_BACKGROUND;
}
}
+
if (schedGroup < ProcessList.SCHED_GROUP_TOP_APP
&& (cr.flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0) {
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
@@ -1304,6 +1309,7 @@
if (!trackedProcState) {
cr.trackProcState(clientProcState, mAdjSeq, now);
}
+
if (procState > clientProcState) {
procState = clientProcState;
app.setCurRawProcState(procState);
@@ -1311,7 +1317,7 @@
adjType = "service";
}
}
- if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND
&& (cr.flags & Context.BIND_SHOWING_UI) != 0) {
app.setPendingUiClean(true);
}
@@ -1366,13 +1372,13 @@
for (int provi = app.pubProviders.size() - 1;
provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- || procState > ActivityManager.PROCESS_STATE_TOP);
+ || procState > PROCESS_STATE_TOP);
provi--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
for (int i = cpr.connections.size() - 1;
i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- || procState > ActivityManager.PROCESS_STATE_TOP);
+ || procState > PROCESS_STATE_TOP);
i--) {
ContentProviderConnection conn = cpr.connections.get(i);
ProcessRecord client = conn.client;
@@ -1392,7 +1398,7 @@
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
- clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ clientProcState = PROCESS_STATE_CACHED_EMPTY;
}
String adjType = null;
if (adj > clientAdj) {
@@ -1407,34 +1413,18 @@
}
app.cached &= client.cached;
}
- if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
- if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
- // Special handling of clients who are in the top state.
- // We *may* want to consider this process to be in the
- // top state as well, but only if there is not another
- // reason for it to be running. Being on the top is a
- // special state, meaning you are specifically running
- // for the current top app. If the process is already
- // running in the background for some other reason, it
- // is more important to continue considering it to be
- // in the background state.
- mayBeTop = true;
- clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- mayBeTopType = adjType = "provider-top";
- mayBeTopSource = client;
- mayBeTopTarget = cpr.name;
+
+ if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
+ if (adjType == null) {
+ adjType = "provider";
+ }
+ if (clientProcState == PROCESS_STATE_TOP) {
+ clientProcState = PROCESS_STATE_BOUND_TOP;
} else {
- // Special handling for above-top states (persistent
- // processes). These should not bring the current process
- // into the top state, since they are not on top. Instead
- // give them the best state after that.
- clientProcState =
- ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- if (adjType == null) {
- adjType = "provider";
- }
+ clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
}
}
+
conn.trackProcState(clientProcState, mAdjSeq, now);
if (procState > clientProcState) {
procState = clientProcState;
@@ -1474,8 +1464,8 @@
"Raise adj to external provider: " + app);
}
}
- if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
app.setCurRawProcState(procState);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
@@ -1507,53 +1497,7 @@
}
}
- if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
- // A client of one of our services or providers is in the top state. We
- // *may* want to be in the top state, but not if we are already running in
- // the background for some other reason. For the decision here, we are going
- // to pick out a few specific states that we want to remain in when a client
- // is top (states that tend to be longer-term) and otherwise allow it to go
- // to the top state.
- switch (procState) {
- case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
- case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
- case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION:
- // Something else is keeping it at this level, just leave it.
- break;
- case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
- case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
- case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
- case ActivityManager.PROCESS_STATE_SERVICE:
- // These all are longer-term states, so pull them up to the top
- // of the background states, but not all the way to the top state.
- procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- app.adjType = mayBeTopType;
- app.adjSource = mayBeTopSource;
- app.adjTarget = mayBeTopTarget;
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
- + ": " + app + ", due to " + mayBeTopSource
- + " adj=" + adj + " procState="
- + ProcessList.makeProcStateString(procState));
- }
- break;
- default:
- // Otherwise, top is a better choice, so take it.
- procState = ActivityManager.PROCESS_STATE_TOP;
- app.adjType = mayBeTopType;
- app.adjSource = mayBeTopSource;
- app.adjTarget = mayBeTopTarget;
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
- + ": " + app + ", due to " + mayBeTopSource
- + " adj=" + adj + " procState="
- + ProcessList.makeProcStateString(procState));
- }
- break;
- }
- }
-
- if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+ if (procState >= PROCESS_STATE_CACHED_EMPTY) {
if (app.hasClientActivities()) {
// This is a cached process, but with client activities. Mark it so.
procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
@@ -1607,8 +1551,8 @@
// Put bound foreground services in a special sched group for additional
// restrictions on screen off
- if (procState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE &&
- mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+ && mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
}
@@ -1905,8 +1849,8 @@
+ " (" + app.getCurProcState() + ")" + ": " + app.adjType;
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
- boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
- boolean curImportant = app.getCurProcState() < ActivityManager.PROCESS_STATE_SERVICE;
+ boolean setImportant = app.setProcState < PROCESS_STATE_SERVICE;
+ boolean curImportant = app.getCurProcState() < PROCESS_STATE_SERVICE;
if (setImportant && !curImportant) {
// This app is no longer something we consider important enough to allow to use
// arbitrary amounts of battery power. Note its current CPU time to later know to
@@ -1969,10 +1913,11 @@
// To avoid some abuse patterns, we are going to be careful about what we consider
// to be an app interaction. Being the top activity doesn't count while the display
// is sleeping, nor do short foreground services.
- if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
+ if (app.getCurProcState() <= PROCESS_STATE_TOP
+ || app.getCurProcState() == PROCESS_STATE_BOUND_TOP) {
isInteraction = true;
app.setFgInteractionTime(0);
- } else if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ } else if (app.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
if (app.getFgInteractionTime() == 0) {
app.setFgInteractionTime(nowElapsed);
isInteraction = false;
@@ -1982,7 +1927,7 @@
}
} else {
isInteraction =
- app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ app.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
app.setFgInteractionTime(0);
}
if (isInteraction
@@ -2004,8 +1949,8 @@
}
private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
- if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
- && app.getCurProcState() > ActivityManager.PROCESS_STATE_TOP) {
+ if (app.setProcState <= PROCESS_STATE_TOP
+ && app.getCurProcState() > PROCESS_STATE_TOP) {
app.lastTopTime = nowUptime;
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index d02fd73..f1f40d4 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -769,6 +769,9 @@
case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION:
procState = "FGSL";
break;
+ case ActivityManager.PROCESS_STATE_BOUND_TOP:
+ procState = "BTOP";
+ break;
case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
procState = "FGS ";
break;
@@ -836,6 +839,9 @@
case ActivityManager.PROCESS_STATE_TOP:
return AppProtoEnums.PROCESS_STATE_TOP;
case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION:
+ return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
+ case ActivityManager.PROCESS_STATE_BOUND_TOP:
+ return AppProtoEnums.PROCESS_STATE_BOUND_TOP;
case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
@@ -966,6 +972,7 @@
PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_TOP
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_BOUND_TOP
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 2e5dd3b..c9e7cfa 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -153,6 +153,7 @@
UID_STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
UID_STATE_FOREGROUND_SERVICE_LOCATION,
// ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION
+ UID_STATE_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_TOP
UID_STATE_FOREGROUND_SERVICE, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
UID_STATE_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
UID_STATE_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 1681c5b..bc78d1a 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -19,6 +19,7 @@
import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
import static android.provider.Settings.System.ADAPTIVE_SLEEP;
import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED;
+import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN;
import android.Manifest;
import android.annotation.NonNull;
@@ -249,6 +250,7 @@
if (userState.mPendingAttentionCheck != null
&& userState.mPendingAttentionCheck.mCallbackInternal.equals(
callbackInternal)) {
+ userState.mPendingAttentionCheck.cancel(ATTENTION_FAILURE_UNKNOWN);
userState.mPendingAttentionCheck = null;
}
return;
@@ -624,7 +626,7 @@
if (userState == null) {
return;
}
- cancel(userState, AttentionService.ATTENTION_FAILURE_UNKNOWN);
+ cancel(userState, ATTENTION_FAILURE_UNKNOWN);
mContext.unbindService(userState.mConnection);
userState.mConnection.cleanupService();
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index c60dd6c..d8e7b7d 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -657,6 +657,7 @@
Slog.e(getTag(), "HAL died");
mMetricsLogger.count(getMetrics().tagHalDied(), 1);
mHALDeathCount++;
+ mCurrentUserId = UserHandle.USER_NULL;
handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /*vendorCode */);
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 fe762c0..5e4bf33 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -46,9 +46,9 @@
import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
-import android.service.restricted_image.RestrictedImagesDumpProto;
import android.service.restricted_image.RestrictedImageProto;
import android.service.restricted_image.RestrictedImageSetProto;
+import android.service.restricted_image.RestrictedImagesDumpProto;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -383,6 +383,12 @@
@Override // Binder call
public void resetLockout(byte[] token) {
checkPermission(MANAGE_BIOMETRIC);
+
+ if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
+ Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
+ return;
+ }
+
try {
mDaemonWrapper.resetLockout(token);
} catch (RemoteException e) {
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 164468e..3d9a47b 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -419,6 +419,12 @@
@Override // Binder call
public void resetTimeout(byte [] token) {
checkPermission(RESET_FINGERPRINT_LOCKOUT);
+
+ if (!FingerprintService.this.hasEnrolledBiometrics(mCurrentUserId)) {
+ Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
+ return;
+ }
+
// TODO: confirm security token when we move timeout management into the HAL layer.
mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 0c55934..da1360d 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -37,7 +37,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.net.INetd;
-import android.net.util.NetdService;
import android.os.Build;
import android.os.INetworkManagementService;
import android.os.RemoteException;
@@ -77,7 +76,8 @@
private final Context mContext;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
- private final INetworkManagementService mNetd;
+ private final INetworkManagementService mNMS;
+ private final INetd mNetd;
// Values are User IDs.
private final Set<Integer> mUsers = new HashSet<>();
@@ -100,6 +100,9 @@
app.requestedPermissionsFlags);
}
}
+ } else {
+ // The last package of this uid is removed from device. Clean the package up.
+ permission = INetd.PERMISSION_UNINSTALLED;
}
return permission;
}
@@ -115,11 +118,12 @@
}
}
- public PermissionMonitor(Context context, INetworkManagementService netd) {
+ public PermissionMonitor(Context context, INetworkManagementService nms, INetd netdService) {
mContext = context;
mPackageManager = context.getPackageManager();
mUserManager = UserManager.get(context);
- mNetd = netd;
+ mNMS = nms;
+ mNetd = netdService;
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -285,11 +289,11 @@
}
try {
if (add) {
- mNetd.setPermission("NETWORK", toIntArray(network));
- mNetd.setPermission("SYSTEM", toIntArray(system));
+ mNMS.setPermission("NETWORK", toIntArray(network));
+ mNMS.setPermission("SYSTEM", toIntArray(system));
} else {
- mNetd.clearPermission(toIntArray(network));
- mNetd.clearPermission(toIntArray(system));
+ mNMS.clearPermission(toIntArray(network));
+ mNMS.clearPermission(toIntArray(system));
}
} catch (RemoteException e) {
loge("Exception when updating permissions: " + e);
@@ -447,7 +451,8 @@
*
* @hide
*/
- private void sendPackagePermissionsForUid(int uid, int permissions) {
+ @VisibleForTesting
+ void sendPackagePermissionsForUid(int uid, int permissions) {
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
netdPermissionsAppIds.put(uid, permissions);
sendPackagePermissionsToNetd(netdPermissionsAppIds);
@@ -462,15 +467,13 @@
*
* @hide
*/
- private void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
- INetd netdService = NetdService.getInstance();
- if (netdService == null) {
- Log.e(TAG, "Failed to get the netd service");
- return;
- }
+ @VisibleForTesting
+ void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
+
ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
+ ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
int permissions = netdPermissionsAppIds.valueAt(i);
@@ -485,8 +488,10 @@
updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
case INetd.NO_PERMISSIONS:
- uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+ noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
+ case INetd.PERMISSION_UNINSTALLED:
+ uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
default:
Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ netdPermissionsAppIds.keyAt(i));
@@ -495,20 +500,24 @@
try {
// TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
if (allPermissionAppIds.size() != 0) {
- netdService.trafficSetNetPermForUids(
+ mNetd.trafficSetNetPermForUids(
INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
ArrayUtils.convertToIntArray(allPermissionAppIds));
}
if (internetPermissionAppIds.size() != 0) {
- netdService.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
ArrayUtils.convertToIntArray(internetPermissionAppIds));
}
if (updateStatsPermissionAppIds.size() != 0) {
- netdService.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
}
+ if (noPermissionAppIds.size() != 0) {
+ mNetd.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
+ ArrayUtils.convertToIntArray(noPermissionAppIds));
+ }
if (uninstalledAppIds.size() != 0) {
- netdService.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UNINSTALLED,
ArrayUtils.convertToIntArray(uninstalledAppIds));
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
index 764a6eb..b0bbd72 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -52,7 +52,6 @@
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.util.ArraySet;
-import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -231,7 +230,7 @@
private void handleNotifyUpstream(boolean isCellular) {
if (DBG) {
- Log.d(TAG, "notifyUpstream: " + isCellular
+ mLog.i("notifyUpstream: " + isCellular
+ ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
+ ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
}
@@ -294,7 +293,7 @@
* masterHandler to avoid race conditions.
*/
public void reevaluateSimCardProvisioning() {
- if (DBG) Log.d(TAG, "reevaluateSimCardProvisioning");
+ if (DBG) mLog.i("reevaluateSimCardProvisioning");
if (!mHandler.getLooper().isCurrentThread()) {
// Except for test, this log should not appear in normal flow.
@@ -351,7 +350,7 @@
* @param type tethering type from ConnectivityManager.TETHERING_{@code *}
*/
protected void runSilentTetherProvisioning(int type) {
- if (DBG) Log.d(TAG, "runSilentTetherProvisioning: " + type);
+ if (DBG) mLog.i("runSilentTetherProvisioning: " + type);
// For silent provisioning, settings would stop tethering when entitlement fail.
ResultReceiver receiver = buildProxyReceiver(type,
false/* notifyFail */, null);
@@ -382,7 +381,7 @@
@VisibleForTesting
protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
- if (DBG) Log.d(TAG, "runUiTetherProvisioning: " + type);
+ if (DBG) mLog.i("runUiTetherProvisioning: " + type);
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
@@ -428,7 +427,7 @@
|| mCellularPermitted.indexOfValue(TETHER_ERROR_NO_ERROR) > -1);
if (DBG) {
- Log.d(TAG, "Cellular permission change from " + oldPermitted
+ mLog.i("Cellular permission change from " + oldPermitted
+ " to " + mCellularUpstreamPermitted);
}
@@ -453,10 +452,8 @@
* @param resultCode Provisioning result
*/
protected void addDownstreamMapping(int type, int resultCode) {
- if (DBG) {
- Log.d(TAG, "addDownstreamMapping: " + type + ", result: " + resultCode
- + " ,TetherTypeRequested: " + mCurrentTethers.contains(type));
- }
+ mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode
+ + " ,TetherTypeRequested: " + mCurrentTethers.contains(type));
if (!mCurrentTethers.contains(type)) return;
mCellularPermitted.put(type, resultCode);
@@ -468,7 +465,7 @@
* @param type tethering type from ConnectivityManager.TETHERING_{@code *}
*/
protected void removeDownstreamMapping(int type) {
- if (DBG) Log.d(TAG, "removeDownstreamMapping: " + type);
+ mLog.i("removeDownstreamMapping: " + type);
mCellularPermitted.delete(type);
evaluateCellularPermission();
}
@@ -617,7 +614,7 @@
*/
private int updateEntitlementCacheValue(int type, int resultCode) {
if (DBG) {
- Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
+ mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode);
}
if (resultCode == TETHER_ERROR_NO_ERROR) {
mEntitlementCacheValue.put(type, resultCode);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 4e4b15f..ba4dcdb 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1317,7 +1317,9 @@
final int procState = ami.getUidProcessState(callingUid);
final boolean isUidActive = ami.isUidActive(callingUid);
- if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+ // Providers bound by a TOP app will get PROCESS_STATE_BOUND_TOP, so include those as well
+ if (procState <= ActivityManager.PROCESS_STATE_TOP
+ || procState == ActivityManager.PROCESS_STATE_BOUND_TOP) {
return ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP;
}
if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || isUidActive) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5abc73e..cec4d69 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -69,11 +69,13 @@
import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
+import android.view.IInputMonitorHost;
import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputMonitor;
import android.view.InputWindowHandle;
import android.view.KeyEvent;
import android.view.PointerIcon;
@@ -202,7 +204,10 @@
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
int displayId);
+ private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
+ int displayId, boolean isGestureMonitor);
private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
+ private static native void nativePilferPointers(long ptr, IBinder token);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
private static native int nativeInjectInputEvent(long ptr, InputEvent event,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
@@ -489,12 +494,43 @@
}
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- nativeRegisterInputChannel(mPtr, inputChannels[0], displayId);
+ // Give the output channel a token just for identity purposes.
+ inputChannels[0].setToken(new Binder());
+ nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, false /*isGestureMonitor*/);
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1];
}
/**
+ * Creates an input monitor that will receive pointer events for the purposes of system-wide
+ * gesture interpretation.
+ *
+ * @param inputChannelName The input channel name.
+ * @param displayId Target display id.
+ * @return The input channel.
+ */
+ @Override // Binder call
+ public InputMonitor monitorGestureInput(String inputChannelName, int displayId) {
+ if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
+ "monitorInputRegion()")) {
+ throw new SecurityException("Requires MONITOR_INPUT permission");
+ }
+
+ Objects.requireNonNull(inputChannelName, "inputChannelName must not be null.");
+
+ if (displayId < Display.DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException("displayId must >= 0.");
+ }
+
+
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
+ InputMonitorHost host = new InputMonitorHost(inputChannels[0]);
+ inputChannels[0].setToken(host.asBinder());
+ nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/);
+ return new InputMonitor(inputChannelName, inputChannels[1], host);
+ }
+
+ /**
* Registers an input channel so that it can be used as an input event target.
* @param inputChannel The input channel to register.
* @param inputWindowHandle The handle of the input window associated with the
@@ -1810,6 +1846,7 @@
// Native callback.
private void onPointerDownOutsideFocus(IBinder touchedToken) {
+ mWindowManagerCallbacks.onPointerDownOutsideFocus(touchedToken);
}
// Native callback.
@@ -2024,6 +2061,14 @@
public int getPointerLayer();
public int getPointerDisplayId();
+
+ /**
+ * Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
+ * occurred on a window that did not have focus.
+ *
+ * @param touchedToken The token for the window that received the input event.
+ */
+ void onPointerDownOutsideFocus(IBinder touchedToken);
}
/**
@@ -2096,6 +2141,28 @@
}
}
+ /**
+ * Interface for the system to handle request from InputMonitors.
+ */
+ private final class InputMonitorHost extends IInputMonitorHost.Stub {
+ private final InputChannel mInputChannel;
+
+ InputMonitorHost(InputChannel channel) {
+ mInputChannel = channel;
+ }
+
+ @Override
+ public void pilferPointers() {
+ nativePilferPointers(mPtr, asBinder());
+ }
+
+ @Override
+ public void dispose() {
+ nativeUnregisterInputChannel(mPtr, mInputChannel);
+ mInputChannel.dispose();
+ }
+ }
+
private static final class KeyboardLayoutDescriptor {
public String packageName;
public String receiverName;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 3e134b2..da836c2 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -94,6 +94,7 @@
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -141,6 +142,7 @@
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -324,7 +326,7 @@
Arrays.fill(newPasswordChars, '\u0000');
final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
setLockCredentialInternal(newPassword, CREDENTIAL_TYPE_PASSWORD, managedUserPassword,
- quality, managedUserId, false);
+ quality, managedUserId, false, /* isLockTiedToParent= */ true);
// We store a private credential for the managed user that's unlocked by the primary
// account holder's credential. As such, the user will never be prompted to enter this
// password directly, so we always store a password.
@@ -1303,13 +1305,13 @@
setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE,
profilePasswordMap.get(managedUserId),
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
- false);
+ false, /* isLockTiedToParent= */ true);
} else {
Slog.wtf(TAG, "clear tied profile challenges, but no password supplied.");
// Supplying null here would lead to untrusted credential change
setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE, null,
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
- true);
+ true, /* isLockTiedToParent= */ true);
}
mStorage.removeChildProfileLock(managedUserId);
removeKeystoreProfileKey(managedUserId);
@@ -1328,6 +1330,67 @@
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
}
+ /**
+ * Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} during an
+ * unlock operation.
+ */
+ private void sendCredentialsOnUnlockIfRequired(
+ int credentialType, @NonNull byte[] credential, int userId) {
+ // Don't send credentials during the factory reset protection flow.
+ if (userId == USER_FRP) {
+ return;
+ }
+
+ // A profile with a unified lock screen stores a randomly generated credential, so skip it.
+ // Its parent will send credentials for the profile, as it stores the unified lock
+ // credential.
+ if (isManagedProfileWithUnifiedLock(userId)) {
+ return;
+ }
+
+ // Send credentials for the user and any child profiles that share its lock screen.
+ for (int profileId : getProfilesWithSameLockScreen(userId)) {
+ mRecoverableKeyStoreManager.lockScreenSecretAvailable(
+ credentialType, credential, profileId);
+ }
+ }
+
+ /**
+ * Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} when its
+ * credentials are set/changed.
+ */
+ private void sendCredentialsOnChangeIfRequired(
+ int credentialType, byte[] credential, int userId, boolean isLockTiedToParent) {
+ // A profile whose lock screen is being tied to its parent's will either have a randomly
+ // generated credential (creation) or null (removal). We rely on the parent to send its
+ // credentials for the profile in both cases as it stores the unified lock credential.
+ if (isLockTiedToParent) {
+ return;
+ }
+
+ // Send credentials for the user and any child profiles that share its lock screen.
+ for (int profileId : getProfilesWithSameLockScreen(userId)) {
+ mRecoverableKeyStoreManager.lockScreenSecretChanged(
+ credentialType, credential, profileId);
+ }
+ }
+
+ /**
+ * Returns all profiles of {@code userId}, including itself, that have the same lock screen
+ * challenge.
+ */
+ private Set<Integer> getProfilesWithSameLockScreen(int userId) {
+ Set<Integer> profiles = new ArraySet<>();
+ for (UserInfo profile : mUserManager.getProfiles(userId)) {
+ if (profile.id == userId
+ || (profile.profileGroupId == userId
+ && isManagedProfileWithUnifiedLock(profile.id))) {
+ profiles.add(profile.id);
+ }
+ }
+ return profiles;
+ }
+
// This method should be called by LockPatternUtil only, all internal methods in this class
// should call setLockCredentialInternal.
@Override
@@ -1342,16 +1405,24 @@
checkWritePermission(userId);
synchronized (mSeparateChallengeLock) {
setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId,
- allowUntrustedChange);
+ allowUntrustedChange, /* isLockTiedToParent= */ false);
setSeparateProfileChallengeEnabledLocked(userId, true, null);
notifyPasswordChanged(userId);
}
+ if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+ // Make sure the profile doesn't get locked straight after setting work challenge.
+ setDeviceUnlockedForUser(userId);
+ }
notifySeparateProfileChallengeChanged(userId);
}
+ /**
+ * @param isLockTiedToParent is {@code true} if {@code userId} is a profile and its new
+ * credentials are being tied to its parent's credentials.
+ */
private void setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
- byte[] savedCredential, int requestedQuality, int userId,
- boolean allowUntrustedChange) throws RemoteException {
+ byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange,
+ boolean isLockTiedToParent) throws RemoteException {
// Normalize savedCredential and credential such that empty string is always represented
// as null.
if (savedCredential == null || savedCredential.length == 0) {
@@ -1363,7 +1434,7 @@
synchronized (mSpManager) {
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
- requestedQuality, userId, allowUntrustedChange);
+ requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
return;
}
}
@@ -1379,7 +1450,8 @@
fixateNewestUserKeyAuth(userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
notifyActivePasswordMetricsAvailable(CREDENTIAL_TYPE_NONE, null, userId);
- mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
+ sendCredentialsOnChangeIfRequired(
+ credentialType, credential, userId, isLockTiedToParent);
return;
}
if (credential == null) {
@@ -1414,7 +1486,7 @@
initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
currentHandle.type, requestedQuality, userId);
spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
- requestedQuality, userId, allowUntrustedChange);
+ requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
return;
}
}
@@ -1432,8 +1504,8 @@
// Refresh the auth token
doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
- mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential,
- userId);
+ sendCredentialsOnChangeIfRequired(
+ credentialType, credential, userId, isLockTiedToParent);
} else {
throw new RemoteException("Failed to enroll " +
(credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
@@ -1674,8 +1746,7 @@
// The user employs synthetic password based credential.
if (response != null) {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
- mRecoverableKeyStoreManager.lockScreenSecretAvailable(credentialType, credential,
- userId);
+ sendCredentialsOnUnlockIfRequired(credentialType, credential, userId);
}
return response;
}
@@ -1709,7 +1780,8 @@
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
if (shouldReEnrollBaseZero) {
setLockCredentialInternal(credential, storedHash.type, credentialToVerify,
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId, false);
+ DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId, false,
+ /* isLockTiedToParent= */ false);
}
}
@@ -1800,12 +1872,12 @@
storedHash.type == CREDENTIAL_TYPE_PATTERN
? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
: DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
- /* TODO(roosa): keep the same password quality */, userId, false);
+ /* TODO(roosa): keep the same password quality */,
+ userId, false, /* isLockTiedToParent= */ false);
if (!hasChallenge) {
notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
// Use credentials to create recoverable keystore snapshot.
- mRecoverableKeyStoreManager.lockScreenSecretAvailable(
- storedHash.type, credential, userId);
+ sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
return VerifyCredentialResponse.OK;
}
// Fall through to get the auth token. Technically this should never happen,
@@ -1835,9 +1907,7 @@
unlockUser(userId, response.getPayload(), secretFromCredential(credential));
if (isManagedProfileWithSeparatedLock(userId)) {
- TrustManager trustManager =
- (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
- trustManager.setDeviceLockedForUser(userId, false);
+ setDeviceUnlockedForUser(userId);
}
int reEnrollQuality = storedHash.type == CREDENTIAL_TYPE_PATTERN
? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
@@ -1845,7 +1915,7 @@
/* TODO(roosa): keep the same password quality */;
if (shouldReEnroll) {
setLockCredentialInternal(credential, storedHash.type, credential,
- reEnrollQuality, userId, false);
+ reEnrollQuality, userId, false, /* isLockTiedToParent= */ false);
} else {
// Now that we've cleared of all required GK migration, let's do the final
// migration to synthetic password.
@@ -1859,8 +1929,7 @@
}
}
// Use credentials to create recoverable keystore snapshot.
- mRecoverableKeyStoreManager.lockScreenSecretAvailable(storedHash.type, credential,
- userId);
+ sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
@@ -2465,9 +2534,7 @@
activateEscrowTokens(authResult.authToken, userId);
if (isManagedProfileWithSeparatedLock(userId)) {
- TrustManager trustManager =
- (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
- trustManager.setDeviceLockedForUser(userId, false);
+ setDeviceUnlockedForUser(userId);
}
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
@@ -2481,6 +2548,11 @@
return response;
}
+ private void setDeviceUnlockedForUser(int userId) {
+ final TrustManager trustManager = mContext.getSystemService(TrustManager.class);
+ trustManager.setDeviceLockedForUser(userId, false);
+ }
+
/**
* Change the user's lockscreen password by creating a new SP blob and update the handle, based
* on an existing authentication token. Even though a new SP blob is created, the underlying
@@ -2549,7 +2621,7 @@
@GuardedBy("mSpManager")
private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
byte[] savedCredential, int requestedQuality, int userId,
- boolean allowUntrustedChange) throws RemoteException {
+ boolean allowUntrustedChange, boolean isLockTiedToParent) throws RemoteException {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
if (isManagedProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed profile has unified lock
@@ -2615,7 +2687,7 @@
// requestedQuality, userId) instead if we still allow untrusted reset that changes
// synthetic password. That would invalidate existing escrow tokens though.
}
- mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
+ sendCredentialsOnChangeIfRequired(credentialType, credential, userId, isLockTiedToParent);
}
/**
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 1f1ba20..5d667b6 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -35,7 +35,6 @@
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
import android.os.Build;
-import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -63,7 +62,7 @@
*/
public final class MediaProjectionManagerService extends SystemService
implements Watchdog.Monitor {
- private static final boolean REQUIRE_FG_SERVICE_FOR_PROJECTION = false;
+ private static final boolean REQUIRE_FG_SERVICE_FOR_PROJECTION = true;
private static final String TAG = "MediaProjectionManagerService";
private final Object mLock = new Object(); // Protects the list of media projections
@@ -146,7 +145,7 @@
return;
}
- if (mProjectionGrant.targetSdkVersion < VERSION_CODES.Q) {
+ if (!mProjectionGrant.requiresForegroundService()) {
return;
}
@@ -294,7 +293,8 @@
throw new IllegalArgumentException("No package matching :" + packageName);
}
- projection = new MediaProjection(type, uid, packageName, ai.targetSdkVersion);
+ projection = new MediaProjection(type, uid, packageName, ai.targetSdkVersion,
+ ai.isPrivilegedApp());
if (isPermanentGrant) {
mAppOps.setMode(AppOpsManager.OP_PROJECT_MEDIA,
projection.uid, projection.packageName, AppOpsManager.MODE_ALLOWED);
@@ -396,19 +396,22 @@
public final int uid;
public final String packageName;
public final UserHandle userHandle;
- public final int targetSdkVersion;
+ private final int mTargetSdkVersion;
+ private final boolean mIsPrivileged;
private IMediaProjectionCallback mCallback;
private IBinder mToken;
private IBinder.DeathRecipient mDeathEater;
private int mType;
- MediaProjection(int type, int uid, String packageName, int targetSdkVersion) {
+ MediaProjection(int type, int uid, String packageName, int targetSdkVersion,
+ boolean isPrivileged) {
mType = type;
this.uid = uid;
this.packageName = packageName;
userHandle = new UserHandle(UserHandle.getUserId(uid));
- this.targetSdkVersion = targetSdkVersion;
+ mTargetSdkVersion = targetSdkVersion;
+ mIsPrivileged = isPrivileged;
}
@Override // Binder call
@@ -466,7 +469,7 @@
}
if (REQUIRE_FG_SERVICE_FOR_PROJECTION
- && targetSdkVersion >= Build.VERSION_CODES.Q
+ && requiresForegroundService()
&& !mActivityManagerInternal.hasRunningForegroundService(
uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
throw new SecurityException("Media projections require a foreground service"
@@ -531,6 +534,10 @@
return new MediaProjectionInfo(packageName, userHandle);
}
+ boolean requiresForegroundService() {
+ return mTargetSdkVersion >= Build.VERSION_CODES.Q && !mIsPrivileged;
+ }
+
public void dump(PrintWriter pw) {
pw.println("(" + packageName + ", uid=" + uid + "): " + typeToString(mType));
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bfab85b..f2e56b5 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -107,6 +108,7 @@
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
+import android.app.Person;
import android.app.StatusBarManager;
import android.app.UriGrantsManager;
import android.app.admin.DeviceAdminInfo;
@@ -4762,11 +4764,36 @@
/**
* Updates the flags for this notification to reflect whether it is a bubble or not.
*/
- private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId) {
+ private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId,
+ NotificationRecord oldRecord) {
Notification notification = r.getNotification();
- boolean canBubble = mPreferencesHelper.areBubblesAllowed(pkg, userId)
+
+ // Does the app want to bubble & have permission to bubble?
+ boolean canBubble = notification.getBubbleMetadata() != null
+ && mPreferencesHelper.areBubblesAllowed(pkg, userId)
&& r.getChannel().canBubble();
- if (notification.getBubbleMetadata() != null && canBubble) {
+
+ // Is the app in the foreground?
+ final boolean appIsForeground =
+ mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
+
+ // Is the notification something we'd allow to bubble?
+ // A call with a foreground service + person
+ ArrayList<Person> peopleList = notification.extras != null
+ ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
+ : null;
+ boolean isForegroundCall = CATEGORY_CALL.equals(notification.category)
+ && (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
+ // OR message style (which always has a person)
+ Class<? extends Notification.Style> style = notification.getNotificationStyle();
+ boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
+ boolean notificationAppropriateToBubble = isMessageStyle
+ || (peopleList != null && !peopleList.isEmpty() && isForegroundCall);
+ // OR something that was previously a bubble & still exists
+ boolean bubbleUpdate = oldRecord != null
+ && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0;
+
+ if (canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate)) {
notification.flags |= FLAG_BUBBLE;
} else {
notification.flags &= ~FLAG_BUBBLE;
@@ -5129,7 +5156,7 @@
final String tag = n.getTag();
// We need to fix the notification up a little for bubbles
- flagNotificationForBubbles(r, pkg, callingUid);
+ flagNotificationForBubbles(r, pkg, callingUid, old);
// Handle grouped notifications and bail out early if we
// can to avoid extracting signals.
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 6e98d6e..51d5acc 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -374,7 +374,7 @@
synchronized (mLock) {
final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
false);
- if (pi != null) {
+ if (pi != null && !pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
if (pi.isOverlayPackage()) {
mImpl.onOverlayPackageAdded(packageName, userId);
@@ -397,7 +397,7 @@
synchronized (mLock) {
final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
false);
- if (pi != null) {
+ if (pi != null && pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
if (pi.isOverlayPackage()) {
mImpl.onOverlayPackageChanged(packageName, userId);
@@ -438,7 +438,7 @@
synchronized (mLock) {
final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
false);
- if (pi != null) {
+ if (pi != null && !pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
if (pi.isOverlayPackage()) {
mImpl.onOverlayPackageReplaced(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4b46374..7f346f5 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -148,51 +148,6 @@
}
}
- int performDexOpt(SharedLibraryInfo info, String[] instructionSets, DexoptOptions options) {
- String classLoaderContext = DexoptUtils.getClassLoaderContext(info);
- final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
- String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
- PackageManagerService.REASON_SHARED);
- int result = DEX_OPT_SKIPPED;
- for (String instructionSet : dexCodeInstructionSets) {
- int dexoptNeeded = getDexoptNeeded(
- info.getPath(), instructionSet, compilerFilter,
- classLoaderContext, false /* newProfile */,
- false /* downgrade */);
- if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
- continue;
- }
- // Special string recognized by installd.
- final String packageName = "*";
- final String outputPath = null;
- int dexFlags = DEXOPT_PUBLIC
- | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
- | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
- dexFlags = adjustDexoptFlags(dexFlags);
- final String uuid = StorageManager.UUID_SYSTEM;
- final String seInfo = null;
- final int targetSdkVersion = 0; // Builtin libraries targets the system's SDK version
- try {
- mInstaller.dexopt(info.getPath(), Process.SYSTEM_UID, packageName,
- instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
- uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
- getReasonName(options.getCompilationReason()));
- // The end result is:
- // - FAILED if any path failed,
- // - PERFORMED if at least one path needed compilation,
- // - SKIPPED when all paths are up to date
- if (result != DEX_OPT_FAILED) {
- result = DEX_OPT_PERFORMED;
- }
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to dexopt", e);
- result = DEX_OPT_FAILED;
- }
- }
- return result;
- }
-
/**
* Performs dexopt on all code paths of the given package.
* It assumes the install lock is held.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 098225f..643c582d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -476,6 +476,7 @@
static final int SCAN_AS_VENDOR = 1 << 20;
static final int SCAN_AS_PRODUCT = 1 << 21;
static final int SCAN_AS_PRODUCT_SERVICES = 1 << 22;
+ static final int SCAN_AS_ODM = 1 << 23;
@IntDef(flag = true, prefix = { "SCAN_" }, value = {
SCAN_NO_DEX,
@@ -594,6 +595,8 @@
private static final String PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
+ private static final String ODM_OVERLAY_DIR = "/odm/overlay";
+
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
@@ -2523,6 +2526,13 @@
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES,
0);
+ scanDirTracedLI(new File(ODM_OVERLAY_DIR),
+ mDefParseFlags
+ | PackageParser.PARSE_IS_SYSTEM_DIR,
+ scanFlags
+ | SCAN_AS_SYSTEM
+ | SCAN_AS_ODM,
+ 0);
mParallelPackageParserCallback.findStaticOverlayPackages();
@@ -9569,7 +9579,7 @@
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName),
libraryOptions);
} else {
- pdo.performDexOpt(info, instructionSets, libraryOptions);
+ // TODO(ngeoffray): Support dexopting system shared libraries.
}
}
}
@@ -10399,6 +10409,7 @@
* <li>{@link #SCAN_AS_PRODUCT_SERVICES}</li>
* <li>{@link #SCAN_AS_INSTANT_APP}</li>
* <li>{@link #SCAN_AS_VIRTUAL_PRELOAD}</li>
+ * <li>{@link #SCAN_AS_ODM}</li>
* </ul>
*/
private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
@@ -10435,6 +10446,10 @@
& ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0) {
scanFlags |= SCAN_AS_PRODUCT_SERVICES;
}
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
+ scanFlags |= SCAN_AS_ODM;
+ }
}
if (pkgSetting != null) {
final int userId = ((user == null) ? 0 : user.getIdentifier());
@@ -11206,6 +11221,10 @@
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES;
}
+ if ((scanFlags & SCAN_AS_ODM) != 0) {
+ pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ODM;
+ }
+
// Check if the package is signed with the same key as the platform package.
if (PLATFORM_PACKAGE_NAME.equals(pkg.packageName) ||
(platformPkg != null && compareSignatures(
@@ -11532,17 +11551,44 @@
" is static but not pre-installed.");
}
- // The only case where we allow installation of a non-system overlay is when
- // its signature is signed with the platform certificate.
- PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if ((platformPkgSetting.signatures.mSigningDetails
- != PackageParser.SigningDetails.UNKNOWN)
- && (compareSignatures(
- platformPkgSetting.signatures.mSigningDetails.signatures,
- pkg.mSigningDetails.signatures)
- != PackageManager.SIGNATURE_MATCH)) {
- throw new PackageManagerException("Overlay " + pkg.packageName +
- " must be signed with the platform certificate.");
+ // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
+ // signed with the platform certificate. Check this in increasing order of
+ // computational cost.
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
+ final PackageSetting platformPkgSetting =
+ mSettings.getPackageLPr("android");
+ if ((platformPkgSetting.signatures.mSigningDetails
+ != PackageParser.SigningDetails.UNKNOWN)
+ && (compareSignatures(
+ platformPkgSetting.signatures.mSigningDetails.signatures,
+ pkg.mSigningDetails.signatures)
+ != PackageManager.SIGNATURE_MATCH)) {
+ throw new PackageManagerException("Overlay " + pkg.packageName
+ + " must target Q or later, "
+ + "or be signed with the platform certificate");
+ }
+ }
+
+ // A non-preloaded overlay package, without <overlay android:targetName>, will
+ // only be used if it is signed with the same certificate as its target. If the
+ // target is already installed, check this here to augment the last line of
+ // defence which is OMS.
+ if (pkg.mOverlayTargetName == null) {
+ final PackageSetting targetPkgSetting =
+ mSettings.getPackageLPr(pkg.mOverlayTarget);
+ if (targetPkgSetting != null) {
+ if ((targetPkgSetting.signatures.mSigningDetails
+ != PackageParser.SigningDetails.UNKNOWN)
+ && (compareSignatures(
+ targetPkgSetting.signatures.mSigningDetails.signatures,
+ pkg.mSigningDetails.signatures)
+ != PackageManager.SIGNATURE_MATCH)) {
+ throw new PackageManagerException("Overlay " + pkg.packageName
+ + " and target " + pkg.mOverlayTarget + " signed with"
+ + " different certificates, and the overlay lacks"
+ + " <overlay android:targetName>");
+ }
+ }
}
}
}
@@ -12128,6 +12174,8 @@
codeRoot = Environment.getProductDirectory();
} else if (FileUtils.contains(Environment.getProductServicesDirectory(), codePath)) {
codeRoot = Environment.getProductServicesDirectory();
+ } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
+ codeRoot = Environment.getOdmDirectory();
} else {
// Unrecognized code path; take its top real segment as the apk root:
// e.g. /something/app/blah.apk => /something
@@ -14849,7 +14897,6 @@
}
}
- // TODO(ruhler) b/112431924: What user? Test for multi-user.
Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
@@ -14871,7 +14918,7 @@
// it will not miss the broadcast.
enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, getUser(),
+ mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
new BroadcastReceiver() {
@Override
@@ -16381,7 +16428,6 @@
sharedLibLatestVersionSetting);
}
}
- prepareScanResultLocked(result);
}
} catch (PackageManagerException e) {
request.installResult.setError("Scanning Failed.", e);
@@ -17217,13 +17263,15 @@
final boolean oem = isOemApp(oldPackage);
final boolean vendor = isVendorApp(oldPackage);
final boolean product = isProductApp(oldPackage);
+ final boolean odm = isOdmApp(oldPackage);
final @ParseFlags int systemParseFlags = parseFlags;
final @ScanFlags int systemScanFlags = scanFlags
| SCAN_AS_SYSTEM
| (privileged ? SCAN_AS_PRIVILEGED : 0)
| (oem ? SCAN_AS_OEM : 0)
| (vendor ? SCAN_AS_VENDOR : 0)
- | (product ? SCAN_AS_PRODUCT : 0);
+ | (product ? SCAN_AS_PRODUCT : 0)
+ | (odm ? SCAN_AS_ODM : 0);
if (DEBUG_INSTALL) {
Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
@@ -17554,6 +17602,10 @@
& ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
}
+ private static boolean isOdmApp(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+ }
+
private static boolean hasDomainURLs(PackageParser.Package pkg) {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
}
@@ -18328,6 +18380,15 @@
return false;
}
+ static boolean locationIsOdm(String path) {
+ try {
+ return path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to access code path " + path);
+ }
+ return false;
+ }
+
/*
* Tries to delete system package.
*/
@@ -18441,6 +18502,9 @@
if (locationIsProductServices(codePathString)) {
scanFlags |= SCAN_AS_PRODUCT_SERVICES;
}
+ if (locationIsOdm(codePathString)) {
+ scanFlags |= SCAN_AS_ODM;
+ }
final File codePath = new File(codePathString);
final PackageParser.Package pkg =
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2c2cc7e..ead09b4 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -152,6 +152,10 @@
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
}
+ public boolean isOdm() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+ }
+
public boolean isSystem() {
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index fbf5439..a24818f 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -64,6 +64,7 @@
| ApplicationInfo.PRIVATE_FLAG_VENDOR
| ApplicationInfo.PRIVATE_FLAG_PRODUCT
| ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
- | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
+ | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
+ | ApplicationInfo.PRIVATE_FLAG_ODM);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6a1f223..4f81fd9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -60,6 +60,7 @@
import android.os.PatternMatcher;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.SELinux;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -776,7 +777,8 @@
| ApplicationInfo.PRIVATE_FLAG_OEM
| ApplicationInfo.PRIVATE_FLAG_VENDOR
| ApplicationInfo.PRIVATE_FLAG_PRODUCT
- | ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES);
+ | ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
+ | ApplicationInfo.PRIVATE_FLAG_ODM);
pkgSetting.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
pkgSetting.pkgPrivateFlags |=
pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
@@ -788,6 +790,8 @@
pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT;
pkgSetting.pkgPrivateFlags |=
pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES;
+ pkgSetting.pkgPrivateFlags |=
+ pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM;
pkgSetting.primaryCpuAbiString = primaryCpuAbi;
pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
if (childPkgNames != null) {
@@ -2650,6 +2654,24 @@
}
void writePackageListLPr(int creatingUserId) {
+ String filename = mPackageListFilename.getAbsolutePath();
+ String ctx = SELinux.fileSelabelLookup(filename);
+ if (ctx == null) {
+ Slog.wtf(TAG, "Failed to get SELinux context for " +
+ mPackageListFilename.getAbsolutePath());
+ }
+
+ if (!SELinux.setFSCreateContext(ctx)) {
+ Slog.wtf(TAG, "Failed to set packages.list SELinux context");
+ }
+ try {
+ writePackageListLPrInternal(creatingUserId);
+ } finally {
+ SELinux.setFSCreateContext(null);
+ }
+ }
+
+ private void writePackageListLPrInternal(int creatingUserId) {
// Only derive GIDs for active users (not dying)
final List<UserInfo> users = UserManagerService.getInstance().getUsers(true);
int[] userIds = new int[users.size()];
@@ -4401,6 +4423,7 @@
ApplicationInfo.PRIVATE_FLAG_PRODUCT, "PRODUCT",
ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES, "PRODUCT_SERVICES",
ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD",
+ ApplicationInfo.PRIVATE_FLAG_ODM, "ODM",
};
void dumpVersionLPr(IndentingPrintWriter pw) {
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index 9f9b797..4f11887 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -71,7 +71,6 @@
private static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness";
private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal;
- private boolean mShouldSetUpTestHarnessMode;
public TestHarnessModeService(Context context) {
super(context);
@@ -89,9 +88,8 @@
setUpTestHarnessMode();
break;
case PHASE_BOOT_COMPLETED:
- disableAutoSync();
- configureSettings();
- showNotification();
+ completeTestHarnessModeSetup();
+ showNotificationIfEnabled();
break;
}
super.onBootPhase(phase);
@@ -104,47 +102,45 @@
// There's no data to apply, so leave it as-is.
return;
}
- PersistentData persistentData;
- try {
- persistentData = PersistentData.fromBytes(testHarnessModeData);
- } catch (SetUpTestHarnessModeException e) {
- Slog.e(TAG, "Failed to set up Test Harness Mode. Bad data.", e);
- return;
- } finally {
- // Clear out the Test Harness Mode data. It's now in memory if successful or we should
- // skip setting up.
- getPersistentDataBlock().clearTestHarnessModeData();
- }
- mShouldSetUpTestHarnessMode = true;
- setUpAdb(persistentData);
+ // If there is data, we should set the device as provisioned, so that we skip the setup
+ // wizard.
setDeviceProvisioned();
+ SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, "1");
}
- private void setUpAdb(PersistentData persistentData) {
- ContentResolver cr = getContext().getContentResolver();
-
- // Disable the TTL for ADB keys before enabling ADB
- Settings.Global.putLong(cr, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, 0);
-
- SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, "1");
- writeAdbKeysFile(persistentData);
+ private void completeTestHarnessModeSetup() {
+ Slog.d(TAG, "Completing Test Harness Mode setup.");
+ byte[] testHarnessModeData = getPersistentDataBlock().getTestHarnessModeData();
+ if (testHarnessModeData == null || testHarnessModeData.length == 0) {
+ // There's no data to apply, so leave it as-is.
+ return;
+ }
+ try {
+ setUpAdbFiles(PersistentData.fromBytes(testHarnessModeData));
+ disableAutoSync();
+ configureSettings();
+ } catch (SetUpTestHarnessModeException e) {
+ Slog.e(TAG, "Failed to set up Test Harness Mode. Bad data.", e);
+ } finally {
+ // Clear out the Test Harness Mode data so that we don't repeat the setup. If it failed
+ // to set up, then retrying without enabling Test Harness Mode should allow it to boot.
+ // If we succeeded setting up, we shouldn't be re-applying the THM steps every boot
+ // anyway.
+ getPersistentDataBlock().clearTestHarnessModeData();
+ }
}
private void disableAutoSync() {
- if (!mShouldSetUpTestHarnessMode) {
- return;
- }
UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser();
ContentResolver
.setMasterSyncAutomaticallyAsUser(false, primaryUser.getUserHandle().getIdentifier());
}
private void configureSettings() {
- if (!mShouldSetUpTestHarnessMode) {
- return;
- }
ContentResolver cr = getContext().getContentResolver();
+ // Disable the TTL for ADB keys before enabling ADB
+ Settings.Global.putLong(cr, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, 0);
Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1);
Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
@@ -155,7 +151,7 @@
Settings.Global.putInt(cr, Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE, 1);
}
- private void writeAdbKeysFile(PersistentData persistentData) {
+ private void setUpAdbFiles(PersistentData persistentData) {
AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class);
writeBytesToFile(persistentData.mAdbKeys, adbManager.getAdbKeysFile().toPath());
@@ -189,7 +185,7 @@
UserHandle.USER_CURRENT);
}
- private void showNotification() {
+ private void showNotificationIfEnabled() {
if (!SystemProperties.getBoolean(TEST_HARNESS_MODE_PROPERTY, false)) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7c12c1e..3b358e8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -949,7 +949,8 @@
final boolean callingUidHasAnyVisibleWindow =
mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
- || callingUidProcState == ActivityManager.PROCESS_STATE_TOP;
+ || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
+ || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
final boolean isCallingUidPersistentSystemProcess = (callingUid == Process.SYSTEM_UID)
|| callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (isCallingUidForeground || isCallingUidPersistentSystemProcess) {
@@ -980,6 +981,11 @@
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
return false;
}
+ // don't abort if the realCallingUid is an associated companion app
+ if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid),
+ realCallingUid)) {
+ return false;
+ }
}
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
@@ -1025,7 +1031,7 @@
}
// don't abort if the callingPackage has companion device
final int callingUserId = UserHandle.getUserId(callingUid);
- if (mService.isAssociatedCompanionApp(callingUserId, callingPackage)) {
+ if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
return false;
}
// don't abort if the callingPackage is temporarily whitelisted
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9a8824f..b64abdb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -443,8 +443,8 @@
// VoiceInteractionManagerService
ComponentName mActiveVoiceInteractionServiceComponent;
- // A map userId and all its companion app packages
- private final Map<Integer, Set<String>> mCompanionAppPackageMap = new ArrayMap<>();
+ // A map userId and all its companion app uids
+ private final Map<Integer, Set<Integer>> mCompanionAppUidsMap = new ArrayMap<>();
VrController mVrController;
KeyguardController mKeyguardController;
@@ -2084,7 +2084,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- getRecentTasks().removeAllVisibleTasks();
+ getRecentTasks().removeAllVisibleTasks(mAmInternal.getCurrentUserId());
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5812,15 +5812,10 @@
}
WindowProcessController getProcessController(int pid, int uid) {
- final ArrayMap<String, SparseArray<WindowProcessController>> pmap = mProcessNames.getMap();
- for (int i = pmap.size()-1; i >= 0; i--) {
- final SparseArray<WindowProcessController> procs = pmap.valueAt(i);
- for (int j = procs.size() - 1; j >= 0; j--) {
- final WindowProcessController proc = procs.valueAt(j);
- if (UserHandle.isApp(uid) && proc.getPid() == pid && proc.mUid == uid) {
- return proc;
- }
- }
+ final WindowProcessController proc = mPidMap.get(pid);
+ if (proc == null) return null;
+ if (UserHandle.isApp(uid) && proc.mUid == uid) {
+ return proc;
}
return null;
}
@@ -5912,12 +5907,12 @@
}
}
- boolean isAssociatedCompanionApp(int userId, String packageName) {
- final Set<String> allPackages = mCompanionAppPackageMap.get(userId);
- if (allPackages == null) {
+ boolean isAssociatedCompanionApp(int userId, int uid) {
+ final Set<Integer> allUids = mCompanionAppUidsMap.get(userId);
+ if (allUids == null) {
return false;
}
- return allPackages.contains(packageName);
+ return allUids.contains(uid);
}
final class H extends Handler {
@@ -7296,13 +7291,16 @@
@Override
public void setCompanionAppPackages(int userId, Set<String> companionAppPackages) {
- // Deep copy all content to make sure we do not rely on the source
- final Set<String> result = new HashSet<>();
+ // Translate package names into UIDs
+ final Set<Integer> result = new HashSet<>();
for (String pkg : companionAppPackages) {
- result.add(pkg);
+ final int uid = getPackageManagerInternalLocked().getPackageUid(pkg, 0, userId);
+ if (uid >= 0) {
+ result.add(uid);
+ }
}
synchronized (mGlobalLock) {
- mCompanionAppPackageMap.put(userId, result);
+ mCompanionAppUidsMap.put(userId, result);
}
}
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f46835e..6b500967 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -6,6 +6,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
import android.os.Debug;
import android.os.IBinder;
@@ -232,6 +233,11 @@
}
}
+ @Override
+ public void onPointerDownOutsideFocus(IBinder touchedToken) {
+ mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
+ }
+
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f85fdb6..d3dba90 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -63,6 +63,7 @@
// When true, need to call updateInputWindowsLw().
private boolean mUpdateInputWindowsNeeded = true;
private boolean mUpdateInputWindowsPending;
+ private boolean mApplyImmediately;
// Currently focused input window handle.
private InputWindowHandle mFocusedInputWindowHandle;
@@ -152,7 +153,7 @@
mService = service;
mDisplayContent = mService.mRoot.getDisplayContent(displayId);
mDisplayId = displayId;
- mInputTransaction = mDisplayContent.getPendingTransaction();
+ mInputTransaction = mService.mTransactionFactory.make();
mHandler = AnimationThread.getHandler();
mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer();
}
@@ -319,6 +320,14 @@
}
}
+ void updateInputWindowsImmediately() {
+ if (mUpdateInputWindowsPending) {
+ mApplyImmediately = true;
+ mUpdateInputWindows.run();
+ mApplyImmediately = false;
+ }
+ }
+
/* Called when the current input focus changes.
* Layer assignment is assumed to be complete by the time this is called.
*/
@@ -433,7 +442,12 @@
wallpaperInputConsumer.show(mInputTransaction, 0);
}
- mDisplayContent.scheduleAnimation();
+ if (mApplyImmediately) {
+ mInputTransaction.apply();
+ } else {
+ mDisplayContent.getPendingTransaction().merge(mInputTransaction);
+ mDisplayContent.scheduleAnimation();
+ }
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 24b0213..d6c7b21 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -653,10 +653,10 @@
}
}
- void removeAllVisibleTasks() {
+ void removeAllVisibleTasks(int userId) {
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskRecord tr = mTasks.get(i);
- if (isVisibleRecentTask(tr)) {
+ if (tr.userId == userId && isVisibleRecentTask(tr)) {
mTasks.remove(i);
notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index f1560d9..144efb4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
-import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -34,25 +32,16 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
import android.app.ActivityOptions;
-import android.app.AppOpsManager;
import android.app.IAssistDataReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
import android.view.IRecentsAnimationRunner;
-import com.android.server.LocalServices;
-import com.android.server.am.AssistDataRequester;
-import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
-import java.util.List;
-
/**
* Manages the recents animation, including the reordering of the stacks for the transition and
* cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
@@ -70,7 +59,6 @@
private final int mCallingPid;
private int mTargetActivityType;
- private AssistDataRequester mAssistDataRequester;
// The stack to restore the target stack behind when the animation is finished
private ActivityStack mRestoreTargetBehindStack;
@@ -135,9 +123,6 @@
mWindowManager.deferSurfaceLayout();
try {
- // Kick off the assist data request in the background before showing the target activity
- requestAssistData(recentsComponent, recentsUid, assistDataReceiver);
-
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
@@ -216,78 +201,12 @@
}
}
- /**
- * Requests assist data for the top visible activities.
- */
- private void requestAssistData(ComponentName recentsComponent, int recentsUid,
- @Deprecated IAssistDataReceiver assistDataReceiver) {
- final AppOpsManager appOpsManager = (AppOpsManager)
- mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
- final List<IBinder> topActivities =
- mService.mRootActivityContainer.getTopVisibleActivities();
- final AssistDataRequester.AssistDataRequesterCallbacks assistDataCallbacks;
- if (assistDataReceiver != null) {
- assistDataCallbacks = new AssistDataReceiverProxy(assistDataReceiver,
- recentsComponent.getPackageName()) {
- @Override
- public void onAssistDataReceivedLocked(Bundle data, int activityIndex,
- int activityCount) {
- // Try to notify the intelligence service first
- final ContentCaptureManagerInternal imService =
- LocalServices.getService(ContentCaptureManagerInternal.class);
- final IBinder activityToken = topActivities.get(activityIndex);
- final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
- if (r != null && (imService == null
- || !imService.sendActivityAssistData(r.mUserId, activityToken, data))) {
- // Otherwise, use the provided assist data receiver
- super.onAssistDataReceivedLocked(data, activityIndex, activityCount);
- }
- }
- };
- } else {
- final ContentCaptureManagerInternal imService =
- LocalServices.getService(ContentCaptureManagerInternal.class);
- if (imService == null) {
- // There is no intelligence service, so there is no point requesting assist data
- return;
- }
-
- assistDataCallbacks = new AssistDataRequester.AssistDataRequesterCallbacks() {
- @Override
- public boolean canHandleReceivedAssistDataLocked() {
- return true;
- }
-
- @Override
- public void onAssistDataReceivedLocked(Bundle data, int activityIndex,
- int activityCount) {
- // Try to notify the intelligence service
- final IBinder activityToken = topActivities.get(activityIndex);
- final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
- if (r != null) {
- imService.sendActivityAssistData(r.mUserId, activityToken, data);
- }
- }
- };
- }
- mAssistDataRequester = new AssistDataRequester(mService.mContext, mWindowManager,
- appOpsManager, assistDataCallbacks, this, OP_ASSIST_STRUCTURE, OP_NONE);
- mAssistDataRequester.requestAutofillData(topActivities,
- recentsUid, recentsComponent.getPackageName());
- }
-
private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
synchronized (mService.mGlobalLock) {
if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+ mWindowManager.getRecentsAnimationController()
+ " reorderMode=" + reorderMode);
- // Cancel the associated assistant data request
- if (mAssistDataRequester != null) {
- mAssistDataRequester.cancel();
- mAssistDataRequester = null;
- }
-
// Unregister for stack order changes
mDefaultDisplay.unregisterStackOrderChangedListener(this);
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 4ff552e..79baab6 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -22,12 +22,9 @@
import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
-import android.os.Handler;
import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -40,67 +37,15 @@
public class TaskTapPointerEventListener implements PointerEventListener {
private final Region mTouchExcludeRegion = new Region();
- private final Region mTmpRegion = new Region();
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
- private final Handler mHandler;
- private final Runnable mMoveDisplayToTop;
private final Rect mTmpRect = new Rect();
private int mPointerIconType = TYPE_NOT_SPECIFIED;
- private int mLastDownX;
- private int mLastDownY;
public TaskTapPointerEventListener(WindowManagerService service,
DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
- mHandler = new Handler(mService.mH.getLooper());
- mMoveDisplayToTop = () -> {
- int x;
- int y;
- synchronized (this) {
- x = mLastDownX;
- y = mLastDownY;
- }
- synchronized (mService.mGlobalLock) {
- if (!mService.mPerDisplayFocusEnabled
- && mService.mRoot.getTopFocusedDisplayContent() != mDisplayContent
- && inputMethodWindowContains(x, y)) {
- // In a single focus system, if the input method window and the input method
- // target window are on the different displays, when the user is tapping on the
- // input method window, we don't move its display to top. Otherwise, the input
- // method target window will lose the focus.
- return;
- }
- final Region windowTapExcludeRegion = Region.obtain();
- mDisplayContent.amendWindowTapExcludeRegion(windowTapExcludeRegion);
- if (windowTapExcludeRegion.contains(x, y)) {
- windowTapExcludeRegion.recycle();
- // The user is tapping on the window tap exclude region. We don't move this
- // display to top. A window tap exclude region, for example, may be set by an
- // ActivityView, and the region would match the bounds of both the ActivityView
- // and the virtual display in it. In this case, we would take the tap that is on
- // the embedded virtual display instead of this display.
- return;
- }
- windowTapExcludeRegion.recycle();
- WindowContainer parent = mDisplayContent.getParent();
- if (parent != null && parent.getTopChild() != mDisplayContent) {
- parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent,
- true /* includingParents */);
- // For compatibility, only the topmost activity is allowed to be resumed for
- // pre-Q app. Ensure the topmost activities are resumed whenever a display is
- // moved to top.
- // TODO(b/123761773): Investigate whether we can move this into
- // RootActivityContainer#updateTopResumedActivityIfNeeded(). Currently, it is
- // risky to do so because it seems possible to resume activities as part of a
- // larger transaction and it's too early to resume based on current order
- // when performing updateTopResumedActivityIfNeeded().
- mDisplayContent.mAcitvityDisplay.ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
- }
- }
- };
}
@Override
@@ -115,9 +60,6 @@
mService.mTaskPositioningController.handleTapOutsideTask(
mDisplayContent, x, y);
}
- mLastDownX = x;
- mLastDownY = y;
- mHandler.post(mMoveDisplayToTop);
}
}
break;
@@ -178,17 +120,4 @@
mTouchExcludeRegion.set(newRegion);
}
}
-
- private int getDisplayId() {
- return mDisplayContent.getDisplayId();
- }
-
- private boolean inputMethodWindowContains(int x, int y) {
- final WindowState inputMethodWindow = mDisplayContent.mInputMethodWindow;
- if (inputMethodWindow == null || !inputMethodWindow.isVisibleLw()) {
- return false;
- }
- inputMethodWindow.getTouchableRegion(mTmpRegion);
- return mTmpRegion.contains(x, y);
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 20d02ee..8dfb02e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -73,6 +73,7 @@
import static com.android.server.LockGuard.INDEX_WINDOW;
import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -4519,6 +4520,7 @@
public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
public static final int ANIMATION_FAILSAFE = 60;
public static final int RECOMPUTE_FOCUS = 61;
+ public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
/**
* Used to denote that an integer field in a message will not be used.
@@ -4910,6 +4912,13 @@
}
break;
}
+ case ON_POINTER_DOWN_OUTSIDE_FOCUS: {
+ synchronized (mGlobalLock) {
+ final IBinder touchedToken = (IBinder) msg.obj;
+ onPointerDownOutsideFocusLocked(touchedToken);
+ }
+ break;
+ }
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -7522,22 +7531,24 @@
@Override
public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
- boolean shouldWaitForAnimComplete = false;
+ boolean shouldWaitForAnimToComplete = false;
if (ev instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent) ev;
- shouldWaitForAnimComplete = keyEvent.getSource() == InputDevice.SOURCE_MOUSE
+ shouldWaitForAnimToComplete = keyEvent.getSource() == InputDevice.SOURCE_MOUSE
|| keyEvent.getAction() == KeyEvent.ACTION_DOWN;
} else if (ev instanceof MotionEvent) {
MotionEvent motionEvent = (MotionEvent) ev;
- shouldWaitForAnimComplete = motionEvent.getSource() == InputDevice.SOURCE_MOUSE
+ shouldWaitForAnimToComplete = motionEvent.getSource() == InputDevice.SOURCE_MOUSE
|| motionEvent.getAction() == MotionEvent.ACTION_DOWN;
}
- if (shouldWaitForAnimComplete) {
+ if (shouldWaitForAnimToComplete) {
waitForAnimationsToComplete();
synchronized (mGlobalLock) {
mWindowPlacerLocked.performSurfacePlacementIfScheduled();
+ mRoot.forAllDisplays(displayContent ->
+ displayContent.getInputMonitor().updateInputWindowsImmediately());
}
new SurfaceControl.Transaction().syncInputWindows().apply(true);
@@ -7569,4 +7580,37 @@
mGlobalLock.notifyAll();
}
}
+
+ private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
+ final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
+ if (touchedWindow == null) {
+ return;
+ }
+
+ final DisplayContent displayContent = touchedWindow.getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+
+ if (!touchedWindow.canReceiveKeys()) {
+ // If the window that received the input event cannot receive keys, don't move the
+ // display it's on to the top since that window won't be able to get focus anyway.
+ return;
+ }
+
+ final WindowContainer parent = displayContent.getParent();
+ if (parent != null && parent.getTopChild() != displayContent) {
+ parent.positionChildAt(WindowContainer.POSITION_TOP, displayContent,
+ true /* includingParents */);
+ // For compatibility, only the topmost activity is allowed to be resumed for pre-Q
+ // app. Ensure the topmost activities are resumed whenever a display is moved to top.
+ // TODO(b/123761773): Investigate whether we can move this into
+ // RootActivityContainer#updateTopResumedActivityIfNeeded(). Currently, it is risky
+ // to do so because it seems possible to resume activities as part of a larger
+ // transaction and it's too early to resume based on current order when performing
+ // updateTopResumedActivityIfNeeded().
+ displayContent.mAcitvityDisplay.ensureActivitiesVisible(null /* starting */,
+ 0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
+ }
+ }
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3d6c868..204a1ea 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -47,6 +47,7 @@
#include <input/PointerController.h>
#include <input/SpriteController.h>
+#include <ui/Region.h>
#include <inputflinger/InputManager.h>
@@ -137,8 +138,6 @@
jmethodID getAffineTransform;
} gTouchCalibrationClassInfo;
-
-
// --- Global functions ---
template<typename T>
@@ -188,7 +187,6 @@
return result;
}
-
// --- NativeInputManager ---
class NativeInputManager : public virtual RefBase,
@@ -207,8 +205,12 @@
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, int32_t displayId);
+ status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
+ int32_t displayId);
+ status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+ status_t pilferPointers(const sp<IBinder>& token);
void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj);
@@ -443,12 +445,24 @@
inputChannel, displayId);
}
+status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
+ const sp<InputChannel>& inputChannel, int32_t displayId, bool isGestureMonitor) {
+ ATRACE_CALL();
+ return mInputManager->getDispatcher()->registerInputMonitor(
+ inputChannel, displayId, isGestureMonitor);
+}
+
status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
}
+status_t NativeInputManager::pilferPointers(const sp<IBinder>& token) {
+ ATRACE_CALL();
+ return mInputManager->getDispatcher()->pilferPointers(token);
+}
+
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -1396,7 +1410,6 @@
throwInputChannelNotInitialized(env);
return;
}
- bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE;
status_t status = im->registerInputChannel(env, inputChannel, displayId);
@@ -1407,10 +1420,33 @@
return;
}
- // If inputWindowHandle is null and displayId >= 0, treat inputChannel as monitor.
- if (!monitor) {
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
- handleInputChannelDisposed, im);
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
+ handleInputChannelDisposed, im);
+}
+
+static void nativeRegisterInputMonitor(JNIEnv* env, jclass /* clazz */,
+ jlong ptr, jobject inputChannelObj, jint displayId, jboolean isGestureMonitor) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
+ inputChannelObj);
+ if (inputChannel == nullptr) {
+ throwInputChannelNotInitialized(env);
+ return;
+ }
+
+ if (displayId == ADISPLAY_ID_NONE) {
+ std::string message = "InputChannel used as a monitor must be associated with a display";
+ jniThrowRuntimeException(env, message.c_str());
+ return;
+ }
+
+ status_t status = im->registerInputMonitor(env, inputChannel, displayId, isGestureMonitor);
+
+ if (status) {
+ std::string message = StringPrintf("Failed to register input channel. status=%d", status);
+ jniThrowRuntimeException(env, message.c_str());
+ return;
}
}
@@ -1435,6 +1471,13 @@
}
}
+static void nativePilferPointers(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject tokenObj) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ sp<IBinder> token = ibinderForJavaObject(env, tokenObj);
+ im->pilferPointers(token);
+}
+
+
static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */,
jlong ptr, jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1697,8 +1740,13 @@
{ "nativeRegisterInputChannel",
"(JLandroid/view/InputChannel;I)V",
(void*) nativeRegisterInputChannel },
+ { "nativeRegisterInputMonitor",
+ "(JLandroid/view/InputChannel;IZ)V",
+ (void*) nativeRegisterInputMonitor},
{ "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V",
(void*) nativeUnregisterInputChannel },
+ { "nativePilferPointers", "(JLandroid/os/IBinder;)V",
+ (void*) nativePilferPointers },
{ "nativeSetInputFilterEnabled", "(JZ)V",
(void*) nativeSetInputFilterEnabled },
{ "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I",
@@ -1792,7 +1840,7 @@
GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
"notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
-
+
GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
"notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b5c845a..d014c0a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -14123,6 +14123,11 @@
@Override
public void installUpdateFromFile(ComponentName admin,
ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE)
+ .setAdmin(admin)
+ .setBoolean(isDeviceAB())
+ .write();
enforceDeviceOwner(admin);
final long id = mInjector.binderClearCallingIdentity();
try {
@@ -14135,11 +14140,6 @@
mContext, updateFileDescriptor, callback, mInjector, mConstants);
}
updateInstaller.startInstallUpdate();
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE)
- .setAdmin(admin)
- .setBoolean(isDeviceAB())
- .write();
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 8f48f5b..f73a285 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -59,6 +59,7 @@
srcs: ["java/**/*.java"],
static_libs: [
"dnsresolver_aidl_interface-java",
+ "ipmemorystore-client",
"netd_aidl_interface-java",
"networkstack-aidl-interfaces-java",
]
diff --git a/services/net/java/android/net/IIpMemoryStore.aidl b/services/net/java/android/net/IIpMemoryStore.aidl
index 6f88dec..63feae6 100644
--- a/services/net/java/android/net/IIpMemoryStore.aidl
+++ b/services/net/java/android/net/IIpMemoryStore.aidl
@@ -20,8 +20,8 @@
import android.net.ipmemorystore.NetworkAttributesParcelable;
import android.net.ipmemorystore.IOnBlobRetrievedListener;
import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
import android.net.ipmemorystore.IOnStatusListener;
/** {@hide} */
@@ -84,7 +84,7 @@
* @param listener The listener that will be invoked to return the answer.
* @return (through the listener) A SameL3NetworkResponse containing the answer and confidence.
*/
- void isSameNetwork(String l2Key1, String l2Key2, IOnSameNetworkResponseListener listener);
+ void isSameNetwork(String l2Key1, String l2Key2, IOnSameL3NetworkResponseListener listener);
/**
* Retrieve the network attributes for a key.
@@ -95,7 +95,7 @@
* @return (through the listener) The network attributes and the L2 key associated with
* the query.
*/
- void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrieved listener);
+ void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrievedListener listener);
/**
* Retrieve previously stored private data.
diff --git a/services/net/java/android/net/IpMemoryStoreClient.java b/services/net/java/android/net/IpMemoryStoreClient.java
index 2f4fdbd..379c017 100644
--- a/services/net/java/android/net/IpMemoryStoreClient.java
+++ b/services/net/java/android/net/IpMemoryStoreClient.java
@@ -20,14 +20,13 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.ipmemorystore.Blob;
-import android.net.ipmemorystore.IOnBlobRetrievedListener;
-import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
-import android.net.ipmemorystore.IOnStatusListener;
import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.OnBlobRetrievedListener;
+import android.net.ipmemorystore.OnL2KeyResponseListener;
+import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.OnSameL3NetworkResponseListener;
+import android.net.ipmemorystore.OnStatusListener;
import android.net.ipmemorystore.Status;
-import android.net.ipmemorystore.StatusParcelable;
import android.os.RemoteException;
import android.util.Log;
@@ -50,12 +49,6 @@
@NonNull
protected abstract IIpMemoryStore getService() throws InterruptedException, ExecutionException;
- protected StatusParcelable internalErrorStatus() {
- final StatusParcelable error = new StatusParcelable();
- error.resultCode = Status.ERROR_UNKNOWN;
- return error;
- }
-
/**
* Store network attributes for a given L2 key.
* If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
@@ -74,12 +67,13 @@
*/
public void storeNetworkAttributes(@NonNull final String l2Key,
@NonNull final NetworkAttributes attributes,
- @Nullable final IOnStatusListener listener) {
+ @Nullable final OnStatusListener listener) {
try {
try {
- getService().storeNetworkAttributes(l2Key, attributes.toParcelable(), listener);
+ getService().storeNetworkAttributes(l2Key, attributes.toParcelable(),
+ OnStatusListener.toAIDL(listener));
} catch (InterruptedException | ExecutionException m) {
- listener.onComplete(internalErrorStatus());
+ listener.onComplete(new Status(Status.ERROR_UNKNOWN));
}
} catch (RemoteException e) {
Log.e(TAG, "Error storing network attributes", e);
@@ -99,12 +93,13 @@
*/
public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
@NonNull final String name, @NonNull final Blob data,
- @Nullable final IOnStatusListener listener) {
+ @Nullable final OnStatusListener listener) {
try {
try {
- getService().storeBlob(l2Key, clientId, name, data, listener);
+ getService().storeBlob(l2Key, clientId, name, data,
+ OnStatusListener.toAIDL(listener));
} catch (InterruptedException | ExecutionException m) {
- listener.onComplete(internalErrorStatus());
+ listener.onComplete(new Status(Status.ERROR_UNKNOWN));
}
} catch (RemoteException e) {
Log.e(TAG, "Error storing blob", e);
@@ -126,12 +121,13 @@
* Through the listener, returns the L2 key if one matched, or null.
*/
public void findL2Key(@NonNull final NetworkAttributes attributes,
- @NonNull final IOnL2KeyResponseListener listener) {
+ @NonNull final OnL2KeyResponseListener listener) {
try {
try {
- getService().findL2Key(attributes.toParcelable(), listener);
+ getService().findL2Key(attributes.toParcelable(),
+ OnL2KeyResponseListener.toAIDL(listener));
} catch (InterruptedException | ExecutionException m) {
- listener.onL2KeyResponse(internalErrorStatus(), null);
+ listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null);
}
} catch (RemoteException e) {
Log.e(TAG, "Error finding L2 Key", e);
@@ -148,12 +144,13 @@
* Through the listener, a SameL3NetworkResponse containing the answer and confidence.
*/
public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
- @NonNull final IOnSameNetworkResponseListener listener) {
+ @NonNull final OnSameL3NetworkResponseListener listener) {
try {
try {
- getService().isSameNetwork(l2Key1, l2Key2, listener);
+ getService().isSameNetwork(l2Key1, l2Key2,
+ OnSameL3NetworkResponseListener.toAIDL(listener));
} catch (InterruptedException | ExecutionException m) {
- listener.onSameNetworkResponse(internalErrorStatus(), null);
+ listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null);
}
} catch (RemoteException e) {
Log.e(TAG, "Error checking for network sameness", e);
@@ -170,12 +167,13 @@
* the query.
*/
public void retrieveNetworkAttributes(@NonNull final String l2Key,
- @NonNull final IOnNetworkAttributesRetrieved listener) {
+ @NonNull final OnNetworkAttributesRetrievedListener listener) {
try {
try {
- getService().retrieveNetworkAttributes(l2Key, listener);
+ getService().retrieveNetworkAttributes(l2Key,
+ OnNetworkAttributesRetrievedListener.toAIDL(listener));
} catch (InterruptedException | ExecutionException m) {
- listener.onNetworkAttributesRetrieved(internalErrorStatus(), null, null);
+ listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null);
}
} catch (RemoteException e) {
Log.e(TAG, "Error retrieving network attributes", e);
@@ -194,12 +192,13 @@
* and the name of the data associated with the query.
*/
public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
- @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+ @NonNull final String name, @NonNull final OnBlobRetrievedListener listener) {
try {
try {
- getService().retrieveBlob(l2Key, clientId, name, listener);
+ getService().retrieveBlob(l2Key, clientId, name,
+ OnBlobRetrievedListener.toAIDL(listener));
} catch (InterruptedException | ExecutionException m) {
- listener.onBlobRetrieved(internalErrorStatus(), null, null, null);
+ listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null);
}
} catch (RemoteException e) {
Log.e(TAG, "Error retrieving blob", e);
diff --git a/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl b/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
similarity index 94%
rename from services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
rename to services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
index fb4ca3b..870e217 100644
--- a/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
+++ b/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
@@ -20,7 +20,7 @@
import android.net.ipmemorystore.StatusParcelable;
/** {@hide} */
-oneway interface IOnNetworkAttributesRetrieved {
+oneway interface IOnNetworkAttributesRetrievedListener {
/**
* Network attributes were fetched for the specified L2 key. While the L2 key will never
* be null, the attributes may be if no data is stored about this L2 key.
diff --git a/services/net/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl b/services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
similarity index 89%
rename from services/net/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
rename to services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
index 294bd3b..b8ccfb9 100644
--- a/services/net/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
+++ b/services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
@@ -20,10 +20,10 @@
import android.net.ipmemorystore.StatusParcelable;
/** {@hide} */
-oneway interface IOnSameNetworkResponseListener {
+oneway interface IOnSameL3NetworkResponseListener {
/**
* The memory store has come up with the answer to a query that was sent.
*/
- void onSameNetworkResponse(in StatusParcelable status,
+ void onSameL3NetworkResponse(in StatusParcelable status,
in SameL3NetworkResponseParcelable response);
}
diff --git a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
new file mode 100644
index 0000000..9685ff6
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return a blob.
+ * @hide
+ */
+public interface OnBlobRetrievedListener {
+ /**
+ * The memory store has come up with the answer to a query that was sent.
+ */
+ void onBlobRetrieved(Status status, String l2Key, String name, Blob blob);
+
+ /** Converts this OnBlobRetrievedListener to a parcelable object */
+ @NonNull
+ static IOnBlobRetrievedListener toAIDL(final OnBlobRetrievedListener listener) {
+ return new IOnBlobRetrievedListener.Stub() {
+ @Override
+ public void onBlobRetrieved(final StatusParcelable statusParcelable, final String l2Key,
+ final String name, final Blob blob) {
+ listener.onBlobRetrieved(new Status(statusParcelable), l2Key, name, blob);
+ }
+ };
+ }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
new file mode 100644
index 0000000..80209c5
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return a L2 key.
+ * @hide
+ */
+public interface OnL2KeyResponseListener {
+ /**
+ * The operation has completed with the specified status.
+ */
+ void onL2KeyResponse(Status status, String l2Key);
+
+ /** Converts this OnL2KeyResponseListener to a parcelable object */
+ @NonNull
+ static IOnL2KeyResponseListener toAIDL(final OnL2KeyResponseListener listener) {
+ return new IOnL2KeyResponseListener.Stub() {
+ @Override
+ public void onL2KeyResponse(final StatusParcelable statusParcelable,
+ final String l2Key) {
+ listener.onL2KeyResponse(new Status(statusParcelable), l2Key);
+ }
+ };
+ }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
new file mode 100644
index 0000000..f0f6f40
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return network attributes.
+ * @hide
+ */
+public interface OnNetworkAttributesRetrievedListener {
+ /**
+ * The memory store has come up with the answer to a query that was sent.
+ */
+ void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attributes);
+
+ /** Converts this OnNetworkAttributesRetrievedListener to a parcelable object */
+ @NonNull
+ static IOnNetworkAttributesRetrievedListener toAIDL(
+ final OnNetworkAttributesRetrievedListener listener) {
+ return new IOnNetworkAttributesRetrievedListener.Stub() {
+ @Override
+ public void onNetworkAttributesRetrieved(final StatusParcelable statusParcelable,
+ final String l2Key,
+ final NetworkAttributesParcelable networkAttributesParcelable) {
+ listener.onNetworkAttributesRetrieved(
+ new Status(statusParcelable), l2Key,
+ new NetworkAttributes(networkAttributesParcelable));
+ }
+ };
+ }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
new file mode 100644
index 0000000..ba1e0e6
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return a response about network sameness.
+ * @hide
+ */
+public interface OnSameL3NetworkResponseListener {
+ /**
+ * The memory store has come up with the answer to a query that was sent.
+ */
+ void onSameL3NetworkResponse(Status status, SameL3NetworkResponse response);
+
+ /** Converts this OnSameL3NetworkResponseListener to a parcelable object */
+ @NonNull
+ static IOnSameL3NetworkResponseListener toAIDL(final OnSameL3NetworkResponseListener listener) {
+ return new IOnSameL3NetworkResponseListener.Stub() {
+ @Override
+ public void onSameL3NetworkResponse(final StatusParcelable statusParcelable,
+ final SameL3NetworkResponseParcelable sameL3NetworkResponseParcelable) {
+ listener.onSameL3NetworkResponse(
+ new Status(statusParcelable),
+ new SameL3NetworkResponse(sameL3NetworkResponseParcelable));
+ }
+ };
+ }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnStatusListener.java b/services/net/java/android/net/ipmemorystore/OnStatusListener.java
new file mode 100644
index 0000000..0de1666
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnStatusListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return a status to a client.
+ * @hide
+ */
+public interface OnStatusListener {
+ /**
+ * The operation has completed with the specified status.
+ */
+ void onComplete(Status status);
+
+ /** Converts this OnStatusListener to a parcelable object */
+ @NonNull
+ static IOnStatusListener toAIDL(final OnStatusListener listener) {
+ return new IOnStatusListener.Stub() {
+ @Override
+ public void onComplete(final StatusParcelable statusParcelable) {
+ listener.onComplete(new Status(statusParcelable));
+ }
+ };
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 2ed25ea..f336497 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -23,10 +23,12 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -46,6 +48,7 @@
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
@@ -149,4 +152,21 @@
assertTrue(mConnection.getServiceInfo().crashed);
verify(mMockKeyEventDispatcher).flush(mConnection);
}
+
+ @Test
+ public void connectedService_notInEnabledServiceList_doNotInitClient()
+ throws RemoteException {
+ IBinder mockBinder = mock(IBinder.class);
+ IAccessibilityServiceClient mockClient = mock(IAccessibilityServiceClient.class);
+ when(mockBinder.queryLocalInterface(any())).thenReturn(mockClient);
+ when(mMockUserState.getEnabledServicesLocked())
+ .thenReturn(Collections.emptySet());
+ setServiceBinding(COMPONENT_NAME);
+
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+ mHandler.sendAllMessages();
+ verify(mMockSystemSupport, times(2)).onClientChangeLocked(false);
+ verify(mockClient, times(0)).init(any(), anyInt(), any());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 2fbeebd..09e20e0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -46,6 +46,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.LocalServices;
+import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.wm.WindowManagerInternal;
import org.mockito.invocation.InvocationOnMock;
@@ -89,6 +90,7 @@
WindowManagerInternal mMockWindowManager;
FakeGsiService mGsiService;
PasswordSlotManagerTestable mPasswordSlotManager;
+ RecoverableKeyStoreManager mRecoverableKeyStoreManager;
protected boolean mHasSecureLockScreen;
@Override
@@ -105,6 +107,7 @@
mMockWindowManager = mock(WindowManagerInternal.class);
mGsiService = new FakeGsiService();
mPasswordSlotManager = new PasswordSlotManagerTestable();
+ mRecoverableKeyStoreManager = mock(RecoverableKeyStoreManager.class);
LocalServices.removeServiceForTest(LockSettingsInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
@@ -141,12 +144,14 @@
mAuthSecretService = mock(IAuthSecret.class);
mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
- mSpManager, mAuthSecretService, mGsiService);
+ mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager);
when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
installChildProfile(MANAGED_PROFILE_USER_ID);
installAndTurnOffChildProfile(TURNED_OFF_PROFILE_USER_ID);
- when(mUserManager.getProfiles(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserProfiles);
+ for (UserInfo profile : mPrimaryUserProfiles) {
+ when(mUserManager.getProfiles(eq(profile.id))).thenReturn(mPrimaryUserProfiles);
+ }
when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO);
final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles);
@@ -173,6 +178,7 @@
private UserInfo installChildProfile(int profileId) {
final UserInfo userInfo = new UserInfo(
profileId, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE);
+ userInfo.profileGroupId = PRIMARY_USER_ID;
mPrimaryUserProfiles.add(userInfo);
when(mUserManager.getUserInfo(eq(profileId))).thenReturn(userInfo);
when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index f4632db..10fb3ba 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -30,6 +30,7 @@
import android.security.keystore.KeyPermanentlyInvalidatedException;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import java.io.FileNotFoundException;
@@ -45,11 +46,13 @@
private SyntheticPasswordManager mSpManager;
private IAuthSecret mAuthSecretService;
private FakeGsiService mGsiService;
+ private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
IActivityManager activityManager, LockPatternUtils lockPatternUtils,
IStorageManager storageManager, SyntheticPasswordManager spManager,
- IAuthSecret authSecretService, FakeGsiService gsiService) {
+ IAuthSecret authSecretService, FakeGsiService gsiService,
+ RecoverableKeyStoreManager recoverableKeyStoreManager) {
super(context);
mLockSettingsStorage = storage;
mKeyStore = keyStore;
@@ -58,6 +61,7 @@
mStorageManager = storageManager;
mSpManager = spManager;
mGsiService = gsiService;
+ mRecoverableKeyStoreManager = recoverableKeyStoreManager;
}
@Override
@@ -119,15 +123,21 @@
public boolean isGsiRunning() {
return mGsiService.isGsiRunning();
}
+
+ @Override
+ public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
+ return mRecoverableKeyStoreManager;
+ }
}
protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
IStorageManager storageManager, IActivityManager mActivityManager,
SyntheticPasswordManager spManager, IAuthSecret authSecretService,
- FakeGsiService gsiService) {
+ FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager) {
super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
- storageManager, spManager, authSecretService, gsiService));
+ storageManager, spManager, authSecretService, gsiService,
+ recoverableKeyStoreManager));
mGateKeeperService = gatekeeper;
mAuthSecretService = authSecretService;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 7ebc745..67d6eda 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -25,6 +25,13 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.gatekeeper.GateKeeperResponse;
@@ -211,6 +218,222 @@
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
}
+ public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception {
+ final byte[] password = "password".getBytes();
+
+ mService.setLockCredential(
+ password,
+ CREDENTIAL_TYPE_PASSWORD,
+ null,
+ PASSWORD_QUALITY_ALPHABETIC,
+ PRIMARY_USER_ID,
+ false);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, password, PRIMARY_USER_ID);
+ }
+
+ public void testSetLockCredential_forProfileWithSeparateChallenge_sendsCredentials()
+ throws Exception {
+ final byte[] pattern = "12345".getBytes();
+
+ mService.setLockCredential(
+ pattern,
+ CREDENTIAL_TYPE_PATTERN,
+ null,
+ PASSWORD_QUALITY_SOMETHING,
+ MANAGED_PROFILE_USER_ID,
+ false);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
+ }
+
+ public void testSetLockCredential_forProfileWithSeparateChallenge_updatesCredentials()
+ throws Exception {
+ final String oldCredential = "12345";
+ final byte[] newCredential = "newPassword".getBytes();
+ initializeStorageWithCredential(
+ MANAGED_PROFILE_USER_ID,
+ oldCredential,
+ CREDENTIAL_TYPE_PATTERN,
+ PASSWORD_QUALITY_SOMETHING);
+
+ mService.setLockCredential(
+ newCredential,
+ CREDENTIAL_TYPE_PASSWORD,
+ oldCredential.getBytes(),
+ PASSWORD_QUALITY_ALPHABETIC,
+ MANAGED_PROFILE_USER_ID,
+ false);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(
+ CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
+ }
+
+ public void testSetLockCredential_forProfileWithUnifiedChallenge_doesNotSendRandomCredential()
+ throws Exception {
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+
+ mService.setLockCredential(
+ "12345".getBytes(),
+ CREDENTIAL_TYPE_PATTERN,
+ null,
+ PASSWORD_QUALITY_SOMETHING,
+ PRIMARY_USER_ID,
+ false);
+
+ verify(mRecoverableKeyStoreManager, never())
+ .lockScreenSecretChanged(
+ eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
+ }
+
+ public void
+ testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_updatesBothCredentials()
+ throws Exception {
+ final String oldCredential = "oldPassword";
+ final byte[] newCredential = "newPassword".getBytes();
+ initializeStorageWithCredential(
+ PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+
+ mService.setLockCredential(
+ newCredential,
+ CREDENTIAL_TYPE_PASSWORD,
+ oldCredential.getBytes(),
+ PASSWORD_QUALITY_ALPHABETIC,
+ PRIMARY_USER_ID,
+ false);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential, PRIMARY_USER_ID);
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(
+ CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
+ }
+
+ public void
+ testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_removesBothCredentials()
+ throws Exception {
+ final String oldCredential = "oldPassword";
+ initializeStorageWithCredential(
+ PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+
+ mService.setLockCredential(
+ null,
+ CREDENTIAL_TYPE_NONE,
+ oldCredential.getBytes(),
+ PASSWORD_QUALITY_UNSPECIFIED,
+ PRIMARY_USER_ID,
+ false);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, PRIMARY_USER_ID);
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, MANAGED_PROFILE_USER_ID);
+ }
+
+ public void testSetLockCredential_forUnifiedToSeparateChallengeProfile_sendsNewCredentials()
+ throws Exception {
+ final String parentPassword = "parentPassword";
+ final byte[] profilePassword = "profilePassword".getBytes();
+ initializeStorageWithCredential(
+ PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234);
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+
+ mService.setLockCredential(
+ profilePassword,
+ CREDENTIAL_TYPE_PASSWORD,
+ null,
+ PASSWORD_QUALITY_ALPHABETIC,
+ MANAGED_PROFILE_USER_ID,
+ false);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(
+ CREDENTIAL_TYPE_PASSWORD, profilePassword, MANAGED_PROFILE_USER_ID);
+ }
+
+ public void
+ testSetLockCredential_forSeparateToUnifiedChallengeProfile_doesNotSendRandomCredential()
+ throws Exception {
+ final String parentPassword = "parentPassword";
+ final String profilePassword = "12345";
+ initializeStorageWithCredential(
+ PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234);
+ // Create and verify separate profile credentials.
+ testCreateCredential(
+ MANAGED_PROFILE_USER_ID,
+ profilePassword,
+ CREDENTIAL_TYPE_PATTERN,
+ PASSWORD_QUALITY_SOMETHING);
+
+ mService.setSeparateProfileChallengeEnabled(
+ MANAGED_PROFILE_USER_ID, false, profilePassword.getBytes());
+
+ // Called once for setting the initial separate profile credentials and not again during
+ // unification.
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretChanged(anyInt(), any(), eq(MANAGED_PROFILE_USER_ID));
+ }
+
+ public void testVerifyCredential_forPrimaryUser_sendsCredentials() throws Exception {
+ final String password = "password";
+ initializeStorageWithCredential(PRIMARY_USER_ID, password, CREDENTIAL_TYPE_PASSWORD, 1234);
+ reset(mRecoverableKeyStoreManager);
+
+ mService.verifyCredential(
+ password.getBytes(), CREDENTIAL_TYPE_PASSWORD, 1, PRIMARY_USER_ID);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretAvailable(
+ CREDENTIAL_TYPE_PASSWORD, password.getBytes(), PRIMARY_USER_ID);
+ }
+
+ public void testVerifyCredential_forProfileWithSeparateChallenge_sendsCredentials()
+ throws Exception {
+ final byte[] pattern = "12345".getBytes();
+ mService.setLockCredential(
+ pattern,
+ CREDENTIAL_TYPE_PATTERN,
+ null,
+ PASSWORD_QUALITY_SOMETHING,
+ MANAGED_PROFILE_USER_ID,
+ false);
+ reset(mRecoverableKeyStoreManager);
+
+ mService.verifyCredential(pattern, CREDENTIAL_TYPE_PATTERN, 1, MANAGED_PROFILE_USER_ID);
+
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretAvailable(
+ CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
+ }
+
+ public void
+ testVerifyCredential_forPrimaryUserWithUnifiedChallengeProfile_sendsCredentialsForBoth()
+ throws Exception {
+ final String pattern = "12345";
+ initializeStorageWithCredential(PRIMARY_USER_ID, pattern, CREDENTIAL_TYPE_PATTERN, 1234);
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+ reset(mRecoverableKeyStoreManager);
+
+ mService.verifyCredential(pattern.getBytes(), CREDENTIAL_TYPE_PATTERN, 1, PRIMARY_USER_ID);
+
+ // Parent sends its credentials for both the parent and profile.
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretAvailable(
+ CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), PRIMARY_USER_ID);
+ verify(mRecoverableKeyStoreManager)
+ .lockScreenSecretAvailable(
+ CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), MANAGED_PROFILE_USER_ID);
+ // Profile doesn't send its own random credentials.
+ verify(mRecoverableKeyStoreManager, never())
+ .lockScreenSecretAvailable(
+ eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
+ }
+
private void testCreateCredential(int userId, String credential, int type, int quality)
throws RemoteException {
mService.setLockCredential(credential.getBytes(), type, null, quality,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 5bab65c..6890017 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -58,6 +58,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.widget.LockPatternUtils;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
@@ -83,7 +84,7 @@
import java.util.ArrayList;
import java.util.Map;
import java.util.Random;
-import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
@@ -156,6 +157,7 @@
@Mock private PlatformKeyManager mPlatformKeyManager;
@Mock private ApplicationKeyStorage mApplicationKeyStorage;
@Mock private CleanupManager mCleanupManager;
+ @Mock private ExecutorService mExecutorService;
@Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -188,7 +190,7 @@
mMockContext,
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
- Executors.newSingleThreadExecutor(),
+ mExecutorService,
mRecoverySnapshotStorage,
mMockListenersStorage,
mPlatformKeyManager,
@@ -1246,6 +1248,24 @@
}
}
+ @Test
+ public void lockScreenSecretAvailable_syncsKeysForUser() throws Exception {
+ mRecoverableKeyStoreManager.lockScreenSecretAvailable(
+ LockPatternUtils.CREDENTIAL_TYPE_PATTERN, "password".getBytes(), 11);
+
+ verify(mExecutorService).execute(any());
+ }
+
+ @Test
+ public void lockScreenSecretChanged_syncsKeysForUser() throws Exception {
+ mRecoverableKeyStoreManager.lockScreenSecretChanged(
+ LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ "password".getBytes(),
+ 11);
+
+ verify(mExecutorService).execute(any());
+ }
+
private static byte[] encryptedApplicationKey(
SecretKey recoveryKey, byte[] applicationKey) throws Exception {
return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 6f1bd87..ca7a71e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,6 +18,8 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
@@ -81,6 +83,7 @@
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.Person;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
@@ -103,6 +106,7 @@
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -433,6 +437,11 @@
private NotificationRecord generateNotificationRecord(NotificationChannel channel,
Notification.TvExtender extender) {
+ return generateNotificationRecord(channel, extender, false /* isBubble */);
+ }
+
+ private NotificationRecord generateNotificationRecord(NotificationChannel channel,
+ Notification.TvExtender extender, boolean isBubble) {
if (channel == null) {
channel = mTestNotificationChannel;
}
@@ -442,6 +451,9 @@
if (extender != null) {
nb.extend(extender);
}
+ if (isBubble) {
+ nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build());
+ }
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
nb.build(), new UserHandle(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
@@ -4293,7 +4305,7 @@
}
@Test
- public void testFlagBubbleNotifs_flagIfAllowed() throws RemoteException {
+ public void testFlagBubbleNotifs_flag_appForeground() throws RemoteException {
// Bubbles are allowed!
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
@@ -4303,12 +4315,260 @@
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
mTestNotificationChannel.getImportance());
- // Notif with bubble metadata
+ // Notif with bubble metadata but not our other misc requirements
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
+ null /* tvExtender */, true /* isBubble */);
+
+ // Say we're foreground
+ when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+ IMPORTANCE_FOREGROUND);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes allowed, yes foreground, yes bubble
+ assertTrue(mService.getNotificationRecord(
+ nr.sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_noFlag_appNotForeground() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Notif with bubble metadata but not our other misc requirements
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
+ null /* tvExtender */, true /* isBubble */);
+
+ // Make sure we're NOT foreground
+ when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+ IMPORTANCE_VISIBLE);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes allowed but NOT foreground, no bubble
+ assertFalse(mService.getNotificationRecord(
+ nr.sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_flag_previousForegroundFlag() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Notif with bubble metadata but not our other misc requirements
+ NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ null /* tvExtender */, true /* isBubble */);
+
+ // Send notif when we're foreground
+ when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
+ IMPORTANCE_FOREGROUND);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
+ waitForIdle();
+
+ // yes allowed, yes foreground, yes bubble
+ assertTrue(mService.getNotificationRecord(
+ nr1.sbn.getKey()).getNotification().isBubbleNotification());
+
+ // Send a new update when we're not foreground
+ NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+ null /* tvExtender */, true /* isBubble */);
+
+ when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
+ IMPORTANCE_VISIBLE);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
+ waitForIdle();
+
+ // yes allowed, previously foreground / flagged, yes bubble
+ assertTrue(mService.getNotificationRecord(
+ nr2.sbn.getKey()).getNotification().isBubbleNotification());
+
+ StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifs2.length);
+ assertEquals(1, mService.getNotificationRecordCount());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_noFlag_previousForegroundFlag_afterRemoval()
+ throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Notif with bubble metadata but not our other misc requirements
+ NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ null /* tvExtender */, true /* isBubble */);
+
+ // Send notif when we're foreground
+ when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
+ IMPORTANCE_FOREGROUND);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
+ waitForIdle();
+
+ // yes allowed, yes foreground, yes bubble
+ assertTrue(mService.getNotificationRecord(
+ nr1.sbn.getKey()).getNotification().isBubbleNotification());
+
+ // Remove the bubble
+ mBinderService.cancelNotificationWithTag(PKG, "tag", nr1.sbn.getId(),
+ nr1.sbn.getUserId());
+ waitForIdle();
+
+ StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+ assertEquals(0, notifs.length);
+ assertEquals(0, mService.getNotificationRecordCount());
+
+ // Send a new update when we're not foreground
+ NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
+ null /* tvExtender */, true /* isBubble */);
+
+ when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
+ IMPORTANCE_VISIBLE);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
+ waitForIdle();
+
+ // yes allowed, but was removed & no foreground, so no bubble
+ assertFalse(mService.getNotificationRecord(
+ nr2.sbn.getKey()).getNotification().isBubbleNotification());
+
+ StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifs2.length);
+ assertEquals(1, mService.getNotificationRecordCount());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_flag_messaging() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Give it bubble metadata
Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // Make it messaging style
Notification.Builder nb = new Notification.Builder(mContext,
mTestNotificationChannel.getId())
.setContentTitle("foo")
.setBubbleMetadata(data)
+ .setStyle(new Notification.MessagingStyle(person)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello?",
+ SystemClock.currentThreadTimeMillis() - 300000, person)
+ .addMessage("Is it me you're looking for?",
+ SystemClock.currentThreadTimeMillis(), person)
+ )
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes allowed, yes messaging, yes bubble
+ assertTrue(mService.getNotificationRecord(
+ sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_flag_phonecall() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Give it bubble metadata
+ Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // Make it a phone call
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setCategory(CATEGORY_CALL)
+ .addPerson(person)
+ .setContentTitle("foo")
+ .setBubbleMetadata(data)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ // Make sure it has foreground service
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes phone call, yes person, yes foreground service, yes bubble
+ assertTrue(mService.getNotificationRecord(
+ sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_noFlag_phonecall_noForegroundService() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Give it bubble metadata
+ Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // Make it a phone call
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setCategory(CATEGORY_CALL)
+ .addPerson(person)
+ .setContentTitle("foo")
+ .setBubbleMetadata(data)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
@@ -4319,13 +4579,89 @@
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
- // yes allowed, yes bubble
- assertTrue(mService.getNotificationRecord(
+ // yes phone call, yes person, NO foreground service, no bubble
+ assertFalse(mService.getNotificationRecord(
sbn.getKey()).getNotification().isBubbleNotification());
}
@Test
- public void testFlagBubbleNotifs_noFlagIfNotAllowed() throws RemoteException {
+ public void testFlagBubbleNotifs_noFlag_phonecall_noPerson() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Give it bubble metadata
+ Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Make it a phone call
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setCategory(CATEGORY_CALL)
+ .setContentTitle("foo")
+ .setBubbleMetadata(data)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ // Make sure it has foreground service
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes phone call, yes foreground service, BUT NO person, no bubble
+ assertFalse(mService.getNotificationRecord(
+ sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_noFlag_phonecall_noCategory() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Give it bubble metadata
+ Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // No category
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .addPerson(person)
+ .setContentTitle("foo")
+ .setBubbleMetadata(data)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ // Make sure it has foreground service
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes person, yes foreground service, BUT NO call, no bubble
+ assertFalse(mService.getNotificationRecord(
+ sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_noFlag_messaging_appNotAllowed() throws RemoteException {
// Bubbles are NOT allowed!
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false);
@@ -4335,12 +4671,24 @@
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
mTestNotificationChannel.getImportance());
- // Notif with bubble metadata
+ // Give it bubble metadata
Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // Make it messaging style
Notification.Builder nb = new Notification.Builder(mContext,
mTestNotificationChannel.getId())
.setContentTitle("foo")
.setBubbleMetadata(data)
+ .setStyle(new Notification.MessagingStyle(person)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello?",
+ SystemClock.currentThreadTimeMillis() - 300000, person)
+ .addMessage("Is it me you're looking for?",
+ SystemClock.currentThreadTimeMillis(), person)
+ )
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
@@ -4358,7 +4706,7 @@
}
@Test
- public void testFlagBubbleNotifs_noFlagIfNotBubble() throws RemoteException {
+ public void testFlagBubbleNotifs_noFlag_notBubble() throws RemoteException {
// Bubbles are allowed!
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
@@ -4369,27 +4717,20 @@
mTestNotificationChannel.getImportance());
// Notif WITHOUT bubble metadata
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
// Post the notification
- mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
// no bubble metadata, no bubble
assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
+ nr.sbn.getKey()).getNotification().isBubbleNotification());
}
@Test
- public void testFlagBubbleNotifs_noFlagIfChannelNotBubble() throws RemoteException {
+ public void testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed() throws RemoteException {
// Bubbles are allowed!
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
@@ -4402,12 +4743,24 @@
// But not on this channel!
mTestNotificationChannel.setAllowBubbles(false);
- // Notif with bubble metadata
+ // Give it bubble metadata
Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // Make it messaging style
Notification.Builder nb = new Notification.Builder(mContext,
mTestNotificationChannel.getId())
.setContentTitle("foo")
.setBubbleMetadata(data)
+ .setStyle(new Notification.MessagingStyle(person)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello?",
+ SystemClock.currentThreadTimeMillis() - 300000, person)
+ .addMessage("Is it me you're looking for?",
+ SystemClock.currentThreadTimeMillis(), person)
+ )
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
@@ -4425,6 +4778,91 @@
}
@Test
+ public void testFlagBubbleNotifs_noFlag_phonecall_notAllowed() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // Give it bubble metadata
+ Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // Make it a phone call
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setCategory(CATEGORY_CALL)
+ .addPerson(person)
+ .setContentTitle("foo")
+ .setBubbleMetadata(data)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ // Make sure it has foreground service
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes phone call, yes person, yes foreground service, but not allowed, no bubble
+ assertFalse(mService.getNotificationRecord(
+ sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed() throws RemoteException {
+ // Bubbles are allowed!
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false);
+ when(mPreferencesHelper.getNotificationChannel(
+ anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ mTestNotificationChannel);
+ when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+ mTestNotificationChannel.getImportance());
+
+ // But not on this channel!
+ mTestNotificationChannel.setAllowBubbles(false);
+
+ // Give it bubble metadata
+ Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+ // Give it a person
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ // Make it a phone call
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setCategory(CATEGORY_CALL)
+ .addPerson(person)
+ .setContentTitle("foo")
+ .setBubbleMetadata(data)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ // Make sure it has foreground service
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // yes phone call, yes person, yes foreground service, but channel not allowed, no bubble
+ assertFalse(mService.getNotificationRecord(
+ sbn.getKey()).getNotification().isBubbleNotification());
+ }
+
+ @Test
public void testCancelAllNotifications_cancelsBubble() throws Exception {
final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
nr.sbn.getNotification().flags |= FLAG_BUBBLE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 08e6ce4..af04858 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -625,7 +625,7 @@
// excludedTask is not trimmed.
assertTrimmed(mTasks.get(0));
- mRecentTasks.removeAllVisibleTasks();
+ mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID);
// Only visible tasks removed.
assertTrimmed(mTasks.get(0), mTasks.get(1), mTasks.get(2), mTasks.get(3));
@@ -849,11 +849,31 @@
mRecentTasks.add(t7);
// Remove all the visible tasks and ensure that they are removed
- mRecentTasks.removeAllVisibleTasks();
+ mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID);
assertTrimmed(t1, t2, t3, t4, t5, t6, t7);
}
@Test
+ public void testRemoveAllVisibleTasksPerUser() {
+ mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
+
+ // Create a visible task per user
+ TaskRecord t1 = createTaskBuilder(".Task1")
+ .setUserId(TEST_USER_0_ID)
+ .build();
+ mRecentTasks.add(t1);
+
+ TaskRecord t2 = createTaskBuilder(".Task1")
+ .setUserId(TEST_USER_1_ID)
+ .build();
+ mRecentTasks.add(t2);
+
+ // Remove all the visible tasks and ensure that they are removed
+ mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID);
+ assertTrimmed(t1);
+ }
+
+ @Test
public void testNotRestoreRecentTaskApis() {
final TaskRecord task = createTaskBuilder(".Task").build();
final int taskId = task.taskId;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c05a346..1822cee 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -552,7 +552,6 @@
private final Bundle mExtras;
private final Bundle mIntentExtras;
private final long mCreationTimeMillis;
- private final CallIdentification mCallIdentification;
private final @CallDirection int mCallDirection;
/**
@@ -738,8 +737,6 @@
* The display name for the caller.
* <p>
* This is the name as reported by the {@link ConnectionService} associated with this call.
- * The name reported by a {@link CallScreeningService} can be retrieved using
- * {@link CallIdentification#getName()}.
*
* @return The display name for the caller.
*/
@@ -857,23 +854,6 @@
}
/**
- * Returns {@link CallIdentification} information provided by a
- * {@link CallScreeningService} for this call.
- * <p>
- * {@link InCallService} implementations should display the {@link CallIdentification} for
- * calls. The name of the call screening service is provided in
- * {@link CallIdentification#getCallScreeningAppName()} and should be used to attribute the
- * call identification information.
- *
- * @return The {@link CallIdentification} if it was provided by a
- * {@link CallScreeningService}, or {@code null} if no {@link CallScreeningService} has
- * provided {@link CallIdentification} information for the call.
- */
- public @Nullable CallIdentification getCallIdentification() {
- return mCallIdentification;
- }
-
- /**
* Indicates whether the call is an incoming or outgoing call.
* @return The call's direction.
*/
@@ -902,7 +882,6 @@
areBundlesEqual(mExtras, d.mExtras) &&
areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
- Objects.equals(mCallIdentification, d.mCallIdentification) &&
Objects.equals(mCallDirection, d.mCallDirection);
}
return false;
@@ -925,7 +904,6 @@
mExtras,
mIntentExtras,
mCreationTimeMillis,
- mCallIdentification,
mCallDirection);
}
@@ -947,7 +925,6 @@
Bundle extras,
Bundle intentExtras,
long creationTimeMillis,
- CallIdentification callIdentification,
int callDirection) {
mTelecomCallId = telecomCallId;
mHandle = handle;
@@ -965,7 +942,6 @@
mExtras = extras;
mIntentExtras = intentExtras;
mCreationTimeMillis = creationTimeMillis;
- mCallIdentification = callIdentification;
mCallDirection = callDirection;
}
@@ -988,7 +964,6 @@
parcelableCall.getExtras(),
parcelableCall.getIntentExtras(),
parcelableCall.getCreationTimeMillis(),
- parcelableCall.getCallIdentification(),
parcelableCall.getCallDirection());
}
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
deleted file mode 100644
index 745affd..0000000
--- a/telecomm/java/android/telecom/CallIdentification.java
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecom;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.graphics.drawable.Icon;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Encapsulates information about an incoming or outgoing {@link Call} provided by a
- * {@link CallScreeningService}.
- * <p>
- * Call identified information is consumed by the {@link InCallService dialer} app to provide the
- * user with more information about a call. This can include information such as the name of the
- * caller, address, etc. Call identification information is persisted to the
- * {@link android.provider.CallLog}.
- */
-public final class CallIdentification implements Parcelable {
- /**
- * Builder for {@link CallIdentification} instances.
- * <p>
- * A {@link CallScreeningService} uses this class to create new instances of
- * {@link CallIdentification} for a screened call.
- */
- public final static class Builder {
- private CharSequence mName;
- private CharSequence mDescription;
- private CharSequence mDetails;
- private Icon mPhoto;
- private int mNuisanceConfidence = CallIdentification.CONFIDENCE_UNKNOWN;
- private String mPackageName;
- private CharSequence mAppName;
-
- /**
- * Default builder constructor.
- */
- public Builder() {
- // Default constructor
- }
-
- /**
- * Create instance of call identification with specified package/app name.
- *
- * @param callIdPackageName The package name.
- * @param callIdAppName The app name.
- * @hide
- */
- public Builder(@NonNull String callIdPackageName, @NonNull CharSequence callIdAppName) {
- mPackageName = callIdPackageName;
- mAppName = callIdAppName;
- }
-
- /**
- * Sets the name associated with the {@link CallIdentification} being built.
- * <p>
- * Could be a business name, for example.
- *
- * @param name The name associated with the call, or {@code null} if none is provided.
- * @return Builder instance.
- */
- public @NonNull Builder setName(@Nullable CharSequence name) {
- mName = name;
- return this;
- }
-
- /**
- * Sets the description associated with the {@link CallIdentification} being built.
- * <p>
- * A description of the call as identified by a {@link CallScreeningService}. The
- * description is typically presented by Dialer apps after the
- * {@link CallIdentification#getName() name} to provide a short piece of relevant
- * information about the call. This could include a location, address, or a message
- * regarding the potential nature of the call (e.g. potential telemarketer).
- *
- * @param description The call description, or {@code null} if none is provided.
- * @return Builder instance.
- */
- public @NonNull Builder setDescription(@Nullable CharSequence description) {
- mDescription = description;
- return this;
- }
-
- /**
- * Sets the details associated with the {@link CallIdentification} being built.
- * <p>
- * The details is typically presented by Dialer apps after the
- * {@link CallIdentification#getName() name} and
- * {@link CallIdentification#getDescription() description} to provide further clarifying
- * information about the call. This could include, for example, the opening hours of a
- * business, or a stats about the number of times a call has been reported as spam.
- *
- * @param details The call details, or {@code null} if none is provided.
- * @return Builder instance.
- */
-
- public @NonNull Builder setDetails(@Nullable CharSequence details) {
- mDetails = details;
- return this;
- }
-
- /**
- * Sets the photo associated with the {@link CallIdentification} being built.
- * <p>
- * This could be, for example, a business logo, or a photo of the caller.
- *
- * @param photo The photo associated with the call, or {@code null} if none was provided.
- * @return Builder instance.
- */
- public @NonNull Builder setPhoto(@Nullable Icon photo) {
- mPhoto = photo;
- return this;
- }
-
- /**
- * Sets the nuisance confidence with the {@link CallIdentification} being built.
- * <p>
- * This can be used to specify how confident the {@link CallScreeningService} is that a call
- * is or is not a nuisance call.
- *
- * @param nuisanceConfidence The nuisance confidence.
- * @return The builder.
- */
- public @NonNull Builder setNuisanceConfidence(@NuisanceConfidence int nuisanceConfidence) {
- mNuisanceConfidence = nuisanceConfidence;
- return this;
- }
-
- /**
- * Creates a new instance of {@link CallIdentification} based on the parameters set in this
- * builder.
- *
- * @return {@link CallIdentification} instance.
- */
- public @NonNull CallIdentification build() {
- return new CallIdentification(mName, mDescription, mDetails, mPhoto,
- mNuisanceConfidence, mPackageName, mAppName);
- }
- }
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(
- prefix = { "CONFIDENCE_" },
- value = {CONFIDENCE_NUISANCE, CONFIDENCE_LIKELY_NUISANCE, CONFIDENCE_UNKNOWN,
- CONFIDENCE_LIKELY_NOT_NUISANCE, CONFIDENCE_NOT_NUISANCE})
- public @interface NuisanceConfidence {}
-
- /**
- * Call has been identified as a nuisance call.
- * <p>
- * Returned from {@link #getNuisanceConfidence()} to indicate that a
- * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
- * nuisance call.
- */
- public static final int CONFIDENCE_NUISANCE = 2;
-
- /**
- * Call has been identified as a likely nuisance call.
- * <p>
- * Returned from {@link #getNuisanceConfidence()} to indicate that a
- * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
- * nuisance call.
- */
- public static final int CONFIDENCE_LIKELY_NUISANCE = 1;
-
- /**
- * Call could not be classified as nuisance or non-nuisance.
- * <p>
- * Returned from {@link #getNuisanceConfidence()} to indicate that a
- * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
- * nuisance call.
- */
- public static final int CONFIDENCE_UNKNOWN = 0;
-
- /**
- * Call has been identified as not likely to be a nuisance call.
- * <p>
- * Returned from {@link #getNuisanceConfidence()} to indicate that a
- * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
- * nuisance call.
- */
- public static final int CONFIDENCE_LIKELY_NOT_NUISANCE = -1;
-
- /**
- * Call has been identified as not a nuisance call.
- * <p>
- * Returned from {@link #getNuisanceConfidence()} to indicate that a
- * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
- * nuisance call.
- */
- public static final int CONFIDENCE_NOT_NUISANCE = -2;
-
- /**
- * Default constructor for {@link CallIdentification}.
- *
- * @param name The name.
- * @param description The description.
- * @param details The details.
- * @param photo The photo.
- * @param nuisanceConfidence Confidence that this is a nuisance call.
- * @hide
- */
- private CallIdentification(@Nullable String name, @Nullable String description,
- @Nullable String details, @Nullable Icon photo,
- @NuisanceConfidence int nuisanceConfidence) {
- this(name, description, details, photo, nuisanceConfidence, null, null);
- }
-
- /**
- * Default constructor for {@link CallIdentification}.
- *
- * @param name The name.
- * @param description The description.
- * @param details The details.
- * @param photo The photo.
- * @param nuisanceConfidence Confidence that this is a nuisance call.
- * @param callScreeningPackageName Package name of the {@link CallScreeningService} which
- * provided the call identification.
- * @param callScreeningAppName App name of the {@link CallScreeningService} which provided the
- * call identification.
- * @hide
- */
- private CallIdentification(@Nullable CharSequence name, @Nullable CharSequence description,
- @Nullable CharSequence details, @Nullable Icon photo,
- @NuisanceConfidence int nuisanceConfidence, @NonNull String callScreeningPackageName,
- @NonNull CharSequence callScreeningAppName) {
- mName = name;
- mDescription = description;
- mDetails = details;
- mPhoto = photo;
- mNuisanceConfidence = nuisanceConfidence;
- mCallScreeningAppName = callScreeningAppName;
- mCallScreeningPackageName = callScreeningPackageName;
- }
-
- private CharSequence mName;
- private CharSequence mDescription;
- private CharSequence mDetails;
- private Icon mPhoto;
- private int mNuisanceConfidence;
- private String mCallScreeningPackageName;
- private CharSequence mCallScreeningAppName;
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int i) {
- parcel.writeCharSequence(mName);
- parcel.writeCharSequence(mDescription);
- parcel.writeCharSequence(mDetails);
- parcel.writeParcelable(mPhoto, 0);
- parcel.writeInt(mNuisanceConfidence);
- parcel.writeString(mCallScreeningPackageName);
- parcel.writeCharSequence(mCallScreeningAppName);
- }
-
- /**
- * Responsible for creating CallIdentification objects for deserialized Parcels.
- */
- public static final @android.annotation.NonNull Parcelable.Creator<CallIdentification> CREATOR =
- new Parcelable.Creator<CallIdentification> () {
-
- @Override
- public CallIdentification createFromParcel(Parcel source) {
- CharSequence name = source.readCharSequence();
- CharSequence description = source.readCharSequence();
- CharSequence details = source.readCharSequence();
- Icon photo = source.readParcelable(ClassLoader.getSystemClassLoader());
- int nuisanceConfidence = source.readInt();
- String callScreeningPackageName = source.readString();
- CharSequence callScreeningAppName = source.readCharSequence();
- return new CallIdentification(name, description, details, photo,
- nuisanceConfidence, callScreeningPackageName, callScreeningAppName);
- }
-
- @Override
- public CallIdentification[] newArray(int size) {
- return new CallIdentification[size];
- }
- };
-
- /**
- * The name associated with the number.
- * <p>
- * The name of the call as identified by a {@link CallScreeningService}. Could be a business
- * name, for example.
- *
- * @return The name associated with the number, or {@code null} if none was provided.
- */
- public final @Nullable CharSequence getName() {
- return mName;
- }
-
- /**
- * Description of the call.
- * <p>
- * A description of the call as identified by a {@link CallScreeningService}. The description
- * is typically presented by Dialer apps after the {@link #getName() name} to provide a short
- * piece of relevant information about the call. This could include a location, address, or a
- * message regarding the potential nature of the call (e.g. potential telemarketer).
- *
- * @return The call description, or {@code null} if none was provided.
- */
- public final @Nullable CharSequence getDescription() {
- return mDescription;
- }
-
- /**
- * Details of the call.
- * <p>
- * Details of the call as identified by a {@link CallScreeningService}. The details
- * are typically presented by Dialer apps after the {@link #getName() name} and
- * {@link #getDescription() description} to provide further clarifying information about the
- * call. This could include, for example, the opening hours of a business, or stats about
- * the number of times a call has been reported as spam.
- *
- * @return The call details, or {@code null} if none was provided.
- */
- public final @Nullable CharSequence getDetails() {
- return mDetails;
- }
-
- /**
- * Photo associated with the call.
- * <p>
- * A photo associated with the call as identified by a {@link CallScreeningService}. This
- * could be, for example, a business logo, or a photo of the caller.
- *
- * @return The photo associated with the call, or {@code null} if none was provided.
- */
- public final @Nullable Icon getPhoto() {
- return mPhoto;
- }
-
- /**
- * Indicates the likelihood that this call is a nuisance call.
- * <p>
- * How likely the call is a nuisance call, as identified by a {@link CallScreeningService}.
- *
- * @return The nuisance confidence.
- */
- public final @NuisanceConfidence int getNuisanceConfidence() {
- return mNuisanceConfidence;
- }
-
- /**
- * The package name of the {@link CallScreeningService} which provided the
- * {@link CallIdentification}.
- * <p>
- * A {@link CallScreeningService} may not set this property; it is set by the system.
- * @return the package name
- */
- public final @NonNull String getCallScreeningPackageName() {
- return mCallScreeningPackageName;
- }
-
- /**
- * The {@link android.content.pm.PackageManager#getApplicationLabel(ApplicationInfo) name} of
- * the {@link CallScreeningService} which provided the {@link CallIdentification}.
- * <p>
- * A {@link CallScreeningService} may not set this property; it is set by the system.
- *
- * @return The name of the app.
- */
- public final @NonNull CharSequence getCallScreeningAppName() {
- return mCallScreeningAppName;
- }
-
- /**
- * Set the package name of the {@link CallScreeningService} which provided this information.
- *
- * @param callScreeningPackageName The package name.
- * @hide
- */
- public void setCallScreeningPackageName(@NonNull String callScreeningPackageName) {
- mCallScreeningPackageName = callScreeningPackageName;
- }
-
- /**
- * Set the app name of the {@link CallScreeningService} which provided this information.
- *
- * @param callScreeningAppName The app name.
- * @hide
- */
- public void setCallScreeningAppName(@NonNull CharSequence callScreeningAppName) {
- mCallScreeningAppName = callScreeningAppName;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- CallIdentification that = (CallIdentification) o;
- // Note: mPhoto purposely omit as no good comparison exists.
- return mNuisanceConfidence == that.mNuisanceConfidence
- && Objects.equals(mName, that.mName)
- && Objects.equals(mDescription, that.mDescription)
- && Objects.equals(mDetails, that.mDetails)
- && Objects.equals(mCallScreeningAppName, that.mCallScreeningAppName)
- && Objects.equals(mCallScreeningPackageName, that.mCallScreeningPackageName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mName, mDescription, mDetails, mPhoto, mNuisanceConfidence,
- mCallScreeningAppName, mCallScreeningPackageName);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("[CallId mName=");
- sb.append(Log.pii(mName));
- sb.append(", mDesc=");
- sb.append(mDescription);
- sb.append(", mDet=");
- sb.append(mDetails);
- sb.append(", conf=");
- sb.append(mNuisanceConfidence);
- sb.append(", appName=");
- sb.append(mCallScreeningAppName);
- sb.append(", pkgName=");
- sb.append(mCallScreeningPackageName);
- return sb.toString();
- }
-}
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index b1aece7..0e0406d 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -39,8 +39,8 @@
/**
* This service can be implemented by the default dialer (see
* {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
- * incoming calls before they are shown to a user. This service can also provide
- * {@link CallIdentification} information for calls.
+ * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see
+ * outgoing calls for the purpose of providing caller ID services for those calls.
* <p>
* Below is an example manifest registration for a {@code CallScreeningService}.
* <pre>
@@ -58,9 +58,9 @@
* <ol>
* <li>Call blocking/screening - the service can choose which calls will ring on the user's
* device, and which will be silently sent to voicemail.</li>
- * <li>Call identification - the service can optionally provide {@link CallIdentification}
- * information about a {@link Call.Details call} which will be shown to the user in the
- * Dialer app.</li>
+ * <li>Call identification - services which provide call identification functionality can
+ * display a user-interface of their choosing which contains identifying information for a call.
+ * </li>
* </ol>
* <p>
* <h2>Becoming the {@link CallScreeningService}</h2>
@@ -92,128 +92,6 @@
* </pre>
*/
public abstract class CallScreeningService extends Service {
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(
- prefix = { "CALL_DURATION_" },
- value = {CALL_DURATION_VERY_SHORT, CALL_DURATION_SHORT, CALL_DURATION_MEDIUM,
- CALL_DURATION_LONG})
- public @interface CallDuration {}
-
- /**
- * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
- * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
- * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
- * {@link CallScreeningService} can use this as a signal for training nuisance detection
- * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
- * identifying call log information to the {@link CallScreeningService}.
- * <p>
- * Indicates the call was < 3 seconds in duration.
- */
- public static final int CALL_DURATION_VERY_SHORT = 1;
-
- /**
- * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
- * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
- * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
- * {@link CallScreeningService} can use this as a signal for training nuisance detection
- * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
- * identifying call log information to the {@link CallScreeningService}.
- * <p>
- * Indicates the call was greater than 3 seconds, but less than 60 seconds in duration.
- */
- public static final int CALL_DURATION_SHORT = 2;
-
- /**
- * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
- * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
- * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
- * {@link CallScreeningService} can use this as a signal for training nuisance detection
- * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
- * identifying call log information to the {@link CallScreeningService}.
- * <p>
- * Indicates the call was greater than 60 seconds, but less than 120 seconds in duration.
- */
- public static final int CALL_DURATION_MEDIUM = 3;
-
- /**
- * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
- * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
- * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
- * {@link CallScreeningService} can use this as a signal for training nuisance detection
- * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
- * identifying call log information to the {@link CallScreeningService}.
- * <p>
- * Indicates the call was greater than 120 seconds.
- */
- public static final int CALL_DURATION_LONG = 4;
-
- /**
- * Telecom sends this intent to the {@link CallScreeningService} which the user has chosen to
- * fill the call screening role when the user indicates through the default dialer whether a
- * call is a nuisance call or not (see
- * {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).
- * <p>
- * The following extra values are provided for the call:
- * <ol>
- * <li>{@link #EXTRA_CALL_HANDLE} - the handle of the call.</li>
- * <li>{@link #EXTRA_IS_NUISANCE} - {@code true} if the user reported the call as a nuisance
- * call, {@code false} otherwise.</li>
- * <li>{@link #EXTRA_CALL_TYPE} - reports the type of call (incoming, rejected, missed,
- * blocked).</li>
- * <li>{@link #EXTRA_CALL_DURATION} - the duration of the call (see
- * {@link #EXTRA_CALL_DURATION} for valid values).</li>
- * </ol>
- * <p>
- * {@link CallScreeningService} implementations which want to track whether the user reports
- * calls are nuisance calls should use declare a broadcast receiver in their manifest for this
- * intent.
- * <p>
- * Note: Only {@link CallScreeningService} implementations which have provided
- * {@link CallIdentification} information for calls at some point will receive this intent.
- */
- public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED =
- "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
-
- /**
- * Extra used to provide the handle of the call for
- * {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}. The call handle is reported as a
- * {@link Uri}.
- */
- public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
-
- /**
- * Boolean extra used to indicate whether the user reported a call as a nuisance call.
- * When {@code true}, the user reported that a call was a nuisance call, {@code false}
- * otherwise. Sent with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}.
- */
- public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
-
- /**
- * Integer extra used with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report the type of
- * call. Valid values are:
- * <UL>
- * <li>{@link android.provider.CallLog.Calls#MISSED_TYPE}</li>
- * <li>{@link android.provider.CallLog.Calls#INCOMING_TYPE}</li>
- * <li>{@link android.provider.CallLog.Calls#BLOCKED_TYPE}</li>
- * <li>{@link android.provider.CallLog.Calls#REJECTED_TYPE}</li>
- * </UL>
- */
- public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
-
- /**
- * Integer extra used to with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report how long
- * the call lasted. Valid values are:
- * <UL>
- * <LI>{@link #CALL_DURATION_VERY_SHORT}</LI>
- * <LI>{@link #CALL_DURATION_SHORT}</LI>
- * <LI>{@link #CALL_DURATION_MEDIUM}</LI>
- * <LI>{@link #CALL_DURATION_LONG}</LI>
- * </UL>
- */
- public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
-
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -413,10 +291,6 @@
* Your app can tell if a call is an incoming call by checking to see if
* {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
* <p>
- * For incoming or outgoing calls, the {@link CallScreeningService} can call
- * {@link #provideCallIdentification(Call.Details, CallIdentification)} in order to provide
- * {@link CallIdentification} for the call.
- * <p>
* Note: The {@link Call.Details} instance provided to a call screening service will only have
* the following properties set. The rest of the {@link Call.Details} properties will be set to
* their default value or {@code null}.
@@ -472,32 +346,4 @@
} catch (RemoteException e) {
}
}
-
- /**
- * Provide {@link CallIdentification} information about a {@link Call.Details call}.
- * <p>
- * The {@link CallScreeningService} calls this method to provide information it has identified
- * about a {@link Call.Details call}. This information will potentially be shown to the user
- * in the {@link InCallService dialer} app. It will be logged to the
- * {@link android.provider.CallLog}.
- * <p>
- * A {@link CallScreeningService} should only call this method for calls for which it is able to
- * provide some {@link CallIdentification} for. {@link CallIdentification} instances with no
- * fields set will be ignored by the system.
- *
- * @param callDetails The call to provide information for.
- * <p>
- * Must be the same {@link Call.Details call} which was provided to the
- * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
- * @param identification An instance of {@link CallIdentification} with information about the
- * {@link Call.Details call}.
- */
- public final void provideCallIdentification(@NonNull Call.Details callDetails,
- @NonNull CallIdentification identification) {
- try {
- mCallScreeningAdapter.provideCallIdentification(callDetails.getTelecomCallId(),
- identification);
- } catch (RemoteException e) {
- }
- }
}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 345707e..aa50991 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -64,7 +64,6 @@
private final Bundle mIntentExtras;
private final Bundle mExtras;
private final long mCreationTimeMillis;
- private final CallIdentification mCallIdentification;
private final int mCallDirection;
public ParcelableCall(
@@ -94,7 +93,6 @@
Bundle intentExtras,
Bundle extras,
long creationTimeMillis,
- CallIdentification callIdentification,
int callDirection) {
mId = id;
mState = state;
@@ -122,7 +120,6 @@
mIntentExtras = intentExtras;
mExtras = extras;
mCreationTimeMillis = creationTimeMillis;
- mCallIdentification = callIdentification;
mCallDirection = callDirection;
}
@@ -314,15 +311,6 @@
}
/**
- * Contains call identification information returned by a {@link CallScreeningService}.
- * @return The {@link CallIdentification} for this call, or {@code null} if a
- * {@link CallScreeningService} did not provide information.
- */
- public @Nullable CallIdentification getCallIdentification() {
- return mCallIdentification;
- }
-
- /**
* Indicates whether the call is an incoming or outgoing call.
*/
public @CallDirection int getCallDirection() {
@@ -366,7 +354,6 @@
boolean isRttCallChanged = source.readByte() == 1;
ParcelableRttCall rttCall = source.readParcelable(classLoader);
long creationTimeMillis = source.readLong();
- CallIdentification callIdentification = source.readParcelable(classLoader);
int callDirection = source.readInt();
return new ParcelableCall(
id,
@@ -395,7 +382,6 @@
intentExtras,
extras,
creationTimeMillis,
- callIdentification,
callDirection);
}
@@ -441,7 +427,6 @@
destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0));
destination.writeParcelable(mRttCall, 0);
destination.writeLong(mCreationTimeMillis);
- destination.writeParcelable(mCallIdentification, 0);
destination.writeInt(mCallDirection);
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3d0a3c5..391d788 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2002,33 +2002,6 @@
}
/**
- * Called by the default dialer to report to Telecom when the user has marked a previous
- * incoming call as a nuisance call or not.
- * <p>
- * Where the user has chosen a {@link CallScreeningService} to fill the call screening role,
- * Telecom will notify that {@link CallScreeningService} of the user's report.
- * <p>
- * Requires that the caller is the default dialer app.
- *
- * @param handle The phone number of an incoming call which the user is reporting as either a
- * nuisance of non-nuisance call.
- * @param isNuisanceCall {@code true} if the user is reporting the call as a nuisance call,
- * {@code false} if the user is reporting the call as a non-nuisance call.
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public void reportNuisanceCallStatus(@NonNull Uri handle, boolean isNuisanceCall) {
- ITelecomService service = getTelecomService();
- if (service != null) {
- try {
- service.reportNuisanceCallStatus(handle, isNuisanceCall,
- mContext.getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
- }
- }
- }
-
- /**
* Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
* @param intent The {@link Intent#ACTION_CALL} intent to handle.
* @hide
diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
index 1160d27..3ee3285 100644
--- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
@@ -17,7 +17,6 @@
package com.android.internal.telecom;
import android.content.ComponentName;
-import android.telecom.CallIdentification;
/**
* Internal remote callback interface for call screening services.
@@ -37,8 +36,4 @@
boolean shouldAddToCallLog,
boolean shouldShowNotification,
in ComponentName componentName);
-
- void provideCallIdentification(
- String callId,
- in CallIdentification callIdentification);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 93eea56..a814c03 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -286,8 +286,6 @@
*/
boolean isInEmergencyCall();
- oneway void reportNuisanceCallStatus(in Uri address, boolean isNuisance, String callingPackage);
-
/**
* @see TelecomServiceImpl#handleCallIntent
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2ee35b2..a567d03 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1896,6 +1896,16 @@
public static final String KEY_IMSI_ENCODING_METHOD_INT = "imsi_encoding_method_int";
/**
+ * Defines the sequence of sending an encrypted IMSI identity for EAP-SIM/AKA authentication.
+ * The value set as below:
+ * 1 - encrypted IMSI as EAP-RESPONSE/IDENTITY (default one).
+ * 2 - anonymous as EAP-RESPONSE/IDENTITY -> encrypted IMSI as EAP-RESPONSE/AKA|SIM-IDENTITY.
+ *
+ * @hide
+ */
+ public static final String KEY_EAP_IDENTITY_SEQUENCE_INT = "imsi_eap_identity_sequence_int";
+
+ /**
* Time delay (in ms) after which we show the notification to switch the preferred
* network.
* @hide
@@ -3054,6 +3064,7 @@
sDefaults.putBoolean(KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL, false);
sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
sDefaults.putInt(KEY_IMSI_ENCODING_METHOD_INT, 2045);
+ sDefaults.putInt(KEY_EAP_IDENTITY_SEQUENCE_INT, 1);
sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 549c044..b75e515 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -2016,7 +2016,16 @@
private static boolean isEmergencyNumberInternal(int subId, String number,
String defaultCountryIso,
boolean useExactMatch) {
- return TelephonyManager.getDefault().isEmergencyNumber(number);
+ try {
+ if (useExactMatch) {
+ return TelephonyManager.getDefault().isEmergencyNumber(number);
+ } else {
+ return TelephonyManager.getDefault().isPotentialEmergencyNumber(number);
+ }
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "isEmergencyNumberInternal: RuntimeException: " + ex);
+ }
+ return false;
}
/**
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index cde10ea..96a2514 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -485,7 +485,7 @@
public boolean isEnabled() {
// In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
// restrictions.
- return getIEuiccController() != null;
+ return getIEuiccController() != null && refreshCardIdIfUninitialized();
}
/**
@@ -499,7 +499,7 @@
*/
@Nullable
public String getEid() {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
return null;
}
try {
@@ -522,7 +522,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public int getOtaStatus() {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
return EUICC_OTA_STATUS_UNAVAILABLE;
}
try {
@@ -557,7 +557,7 @@
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void downloadSubscription(DownloadableSubscription subscription,
boolean switchAfterDownload, PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -619,7 +619,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
PendingIntent callbackIntent =
resolutionIntent.getParcelableExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
@@ -656,7 +656,7 @@
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDownloadableSubscriptionMetadata(
DownloadableSubscription subscription, PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -686,7 +686,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -705,7 +705,7 @@
*/
@Nullable
public EuiccInfo getEuiccInfo() {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
return null;
}
try {
@@ -730,7 +730,7 @@
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -770,7 +770,7 @@
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -796,7 +796,7 @@
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void updateSubscriptionNickname(
int subscriptionId, @Nullable String nickname, @NonNull PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -820,7 +820,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void eraseSubscriptions(PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -850,7 +850,7 @@
* @hide
*/
public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
- if (!refreshCardIdIfUninitialized()) {
+ if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 354c08f..106cd1f 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -20,10 +20,13 @@
import static android.Manifest.permission.CHANGE_WIFI_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.NETWORK_STACK;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_OEM;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.os.Process.SYSTEM_UID;
@@ -41,26 +44,35 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageList;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.INetd;
import android.os.Build;
import android.os.INetworkManagementService;
import android.os.UserHandle;
+import android.util.SparseIntArray;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.LocalServices;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
+import java.util.ArrayList;
import java.util.HashMap;
@RunWith(AndroidJUnit4.class)
@@ -69,7 +81,11 @@
private static final int MOCK_USER1 = 0;
private static final int MOCK_USER2 = 1;
private static final int MOCK_UID1 = 10001;
+ private static final int MOCK_UID2 = 10086;
+ private static final int SYSTEM_UID1 = 1000;
+ private static final int SYSTEM_UID2 = 1008;
private static final String MOCK_PACKAGE1 = "appName1";
+ private static final String MOCK_PACKAGE2 = "appName2";
private static final String SYSTEM_PACKAGE1 = "sysName1";
private static final String SYSTEM_PACKAGE2 = "sysName2";
private static final String PARTITION_SYSTEM = "system";
@@ -82,14 +98,29 @@
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
@Mock private INetworkManagementService mNMS;
+ @Mock private INetd mNetdService;
+ @Mock private PackageManagerInternal mMockPmi;
+ private PackageManagerInternal.PackageListObserver mObserver;
private PermissionMonitor mPermissionMonitor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
+ mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS, mNetdService));
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mMockPmi);
+ when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(new ArrayList<String>(),
+ /* observer */ null));
+ when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
+ mPermissionMonitor.startMonitoring();
+
+ final ArgumentCaptor<PackageManagerInternal.PackageListObserver> observerCaptor =
+ ArgumentCaptor.forClass(PackageManagerInternal.PackageListObserver.class);
+ verify(mMockPmi).getPackageList(observerCaptor.capture());
+ mObserver = observerCaptor.getValue();
}
private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
@@ -104,9 +135,20 @@
}
private PackageInfo packageInfoWithPermissions(String[] permissions, String partition) {
+ int[] requestedPermissionsFlags = new int[permissions.length];
+ for (int i = 0; i < permissions.length; i++) {
+ requestedPermissionsFlags[i] = REQUESTED_PERMISSION_GRANTED;
+ }
+ return packageInfoWithPermissions(permissions, partition,
+ requestedPermissionsFlags);
+ }
+
+ private PackageInfo packageInfoWithPermissions(String[] permissions, String partition,
+ int[] requestedPermissionsFlags) {
final PackageInfo packageInfo = new PackageInfo();
packageInfo.requestedPermissions = permissions;
packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.requestedPermissionsFlags = requestedPermissionsFlags;
int privateFlags = 0;
switch (partition) {
case PARTITION_OEM:
@@ -337,4 +379,164 @@
mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
}
}
+
+ private class NetdServiceMonitor {
+ private final HashMap<Integer, Integer> mPermissions = new HashMap<>();
+
+ NetdServiceMonitor(INetd mockNetdService) throws Exception {
+ // Add hook to verify and track result of setPermission.
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ final int permission = (int) args[0];
+ for (final int uid : (int[]) args[1]) {
+ mPermissions.put(uid, permission);
+ }
+ return null;
+ }).when(mockNetdService).trafficSetNetPermForUids(anyInt(), any(int[].class));
+ }
+
+ public void expectPermission(int permission, int[] apps) {
+ for (final int app : apps) {
+ if (!mPermissions.containsKey(app)) {
+ fail("uid " + app + " does not exist.");
+ }
+ if (mPermissions.get(app) != permission) {
+ fail("uid " + app + " has wrong permission: " + mPermissions.get(app));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testPackagePermissionUpdate() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+ // MOCK_UID1: MOCK_PACKAGE1 only has internet permission.
+ // MOCK_UID2: MOCK_PACKAGE2 does not have any permission.
+ // SYSTEM_UID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission.
+ // SYSTEM_UID2: SYSTEM_PACKAGE2 has only update device stats permission.
+
+ SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+ netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET);
+ netdPermissionsAppIds.put(MOCK_UID2, INetd.NO_PERMISSIONS);
+ netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS);
+ netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS);
+
+ // Send the permission information to netd, expect permission updated.
+ mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds);
+
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET,
+ new int[]{MOCK_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.NO_PERMISSIONS, new int[]{MOCK_UID2});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ new int[]{SYSTEM_UID2});
+
+ // Update permission of MOCK_UID1, expect new permission show up.
+ mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1,
+ INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+
+ // Change permissions of SYSTEM_UID2, expect new permission show up and old permission
+ // revoked.
+ mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2,
+ INetd.PERMISSION_INTERNET);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2});
+
+ // Revoke permission from SYSTEM_UID1, expect no permission stored.
+ mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.NO_PERMISSIONS);
+ mNetdServiceMonitor.expectPermission(INetd.NO_PERMISSIONS, new int[]{SYSTEM_UID1});
+ }
+
+ private PackageInfo addPackage(String packageName, int uid, String[] permissions)
+ throws Exception {
+ PackageInfo packageInfo = packageInfoWithPermissions(permissions, PARTITION_SYSTEM);
+ when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo);
+ when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName});
+ mObserver.onPackageAdded(packageName, uid);
+ return packageInfo;
+ }
+
+ @Test
+ public void testPackageInstall() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+ addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+
+ addPackage(MOCK_PACKAGE2, MOCK_UID2, new String[] {INTERNET});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID2});
+ }
+
+ @Test
+ public void testPackageInstallSharedUid() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+ PackageInfo packageInfo1 = addPackage(MOCK_PACKAGE1, MOCK_UID1,
+ new String[] {INTERNET, UPDATE_DEVICE_STATS});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+
+ // Install another package with the same uid and no permissions should not cause the UID to
+ // lose permissions.
+ PackageInfo packageInfo2 = packageInfoWithPermissions(new String[]{}, PARTITION_SYSTEM);
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
+ when(mPackageManager.getPackagesForUid(MOCK_UID1))
+ .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
+ mObserver.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+ }
+
+ @Test
+ public void testPackageUninstallBasic() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+ addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+
+ when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
+ mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
+ }
+
+ @Test
+ public void testPackageUpdate() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+ addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+
+ // Remove and install the same package to simulate the update action
+ when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
+ mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
+
+ addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
+ }
+
+ @Test
+ public void testPackageUninstallWithMultiplePackages() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+ addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+ | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+
+ // Mock another package with the same uid but different permissions.
+ PackageInfo packageInfo2 = packageInfoWithPermissions(new String[] {INTERNET},
+ PARTITION_SYSTEM);
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
+ when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{
+ MOCK_PACKAGE2});
+
+ mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 36a1b7c..2140322 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -122,7 +122,7 @@
mMockContext = new MockContext(mContext);
}
- private TetheringConfiguration getTetheringConfiguration(int[] legacyTetherUpstreamTypes) {
+ private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
legacyTetherUpstreamTypes);
return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -143,13 +143,13 @@
public void testDunFromTelephonyManagerMeansDun() {
when(mTelephonyManager.getTetherApnRequired()).thenReturn(true);
- final TetheringConfiguration cfgWifi = getTetheringConfiguration(new int[]{TYPE_WIFI});
+ final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
- new int[]{TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI});
+ TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
- new int[]{TYPE_WIFI, TYPE_MOBILE_DUN});
+ TYPE_WIFI, TYPE_MOBILE_DUN);
final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
- new int[]{TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN});
+ TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri,
cfgWifiDun, cfgMobileWifiHipriDun)) {
@@ -167,20 +167,20 @@
public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
when(mTelephonyManager.getTetherApnRequired()).thenReturn(false);
- final TetheringConfiguration cfgWifi = getTetheringConfiguration(new int[]{TYPE_WIFI});
+ final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
- new int[]{TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI});
+ TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
- new int[]{TYPE_WIFI, TYPE_MOBILE_DUN});
+ TYPE_WIFI, TYPE_MOBILE_DUN);
final TetheringConfiguration cfgWifiMobile = getTetheringConfiguration(
- new int[]{TYPE_WIFI, TYPE_MOBILE});
+ TYPE_WIFI, TYPE_MOBILE);
final TetheringConfiguration cfgWifiHipri = getTetheringConfiguration(
- new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
+ TYPE_WIFI, TYPE_MOBILE_HIPRI);
final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
- new int[]{TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN});
+ TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
String msg;
- // TYPE_MOBILE_DUN should not be present in all of the combinations.
+ // TYPE_MOBILE_DUN should be present in none of the combinations.
// TYPE_WIFI should not be affected.
for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun,
cfgWifiMobile, cfgWifiHipri, cfgMobileWifiHipriDun)) {
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index fb84611..a83faf3 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.net.ipmemorystore;
+package com.android.server.connectivity.ipmemorystore;
import static org.junit.Assert.assertEquals;
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index f576745..4262017 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -374,12 +374,11 @@
}
/**
- * Create a network suggestion object use in
+ * Create a network suggestion object for use in
* {@link WifiManager#addNetworkSuggestions(List)}.
*
- * See {@link WifiNetworkSuggestion}.
- *<p>
- * Note: Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID
+ *<p class="note">
+ * <b>Note:</b> Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID
* using {@link #setBssid(MacAddress)} to provide more fine grained network suggestions to
* the platform.
* </p>
@@ -387,7 +386,8 @@
* For example:
* To provide credentials for one open, one WPA2 and one WPA3 network with their
* corresponding SSID's:
- * {@code
+ *
+ * <pre>{@code
* final WifiNetworkSuggestion suggestion1 =
* new Builder()
* .setSsid("test111111")
@@ -403,19 +403,20 @@
* .setWpa3Passphrase("test6789")
* .build()
* final List<WifiNetworkSuggestion> suggestionsList =
- * new ArrayList<WifiNetworkSuggestion> {{
+ * new ArrayList<WifiNetworkSuggestion> { {
* add(suggestion1);
* add(suggestion2);
* add(suggestion3);
- * }};
+ * } };
* final WifiManager wifiManager =
* context.getSystemService(Context.WIFI_SERVICE);
* wifiManager.addNetworkSuggestions(suggestionsList);
- * ...
- * }
+ * // ...
+ * }</pre>
*
- * @return Instance of {@link WifiNetworkSuggestion}.
- * @throws IllegalStateException on invalid params set.
+ * @return Instance of {@link WifiNetworkSuggestion}
+ * @throws IllegalStateException on invalid params set
+ * @see WifiNetworkSuggestion
*/
public @NonNull WifiNetworkSuggestion build() {
if (mSsid == null) {