Merge "LocalDrive fails fast on P devices"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 478e4fe..d01e183 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -251,6 +251,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/api/current.txt b/api/current.txt
index 78d7530..38bf3ab 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4433,11 +4433,12 @@
}
public final class AutomaticZenRule implements android.os.Parcelable {
- ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
- ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, android.service.notification.ZenPolicy, boolean);
+ ctor public deprecated AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
+ ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.content.ComponentName, android.net.Uri, android.service.notification.ZenPolicy, int, boolean);
ctor public AutomaticZenRule(android.os.Parcel);
method public int describeContents();
method public android.net.Uri getConditionId();
+ method public android.content.ComponentName getConfigurationActivity();
method public long getCreationTime();
method public int getInterruptionFilter();
method public java.lang.String getName();
@@ -4445,6 +4446,7 @@
method public android.service.notification.ZenPolicy getZenPolicy();
method public boolean isEnabled();
method public void setConditionId(android.net.Uri);
+ method public void setConfigurationActivity(android.content.ComponentName);
method public void setEnabled(boolean);
method public void setInterruptionFilter(int);
method public void setName(java.lang.String);
@@ -5773,16 +5775,19 @@
method public void notifyAsPackage(java.lang.String, java.lang.String, int, android.app.Notification);
method public boolean removeAutomaticZenRule(java.lang.String);
method public void revokeNotificationDelegate();
+ method public void setAutomaticZenRuleState(java.lang.String, android.service.notification.Condition);
method public final void setInterruptionFilter(int);
method public void setNotificationDelegate(java.lang.String);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_APP_BLOCK_STATE_CHANGED = "android.app.action.APP_BLOCK_STATE_CHANGED";
+ field public static final java.lang.String ACTION_AUTOMATIC_ZEN_RULE = "android.app.action.AUTOMATIC_ZEN_RULE";
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
+ field public static final java.lang.String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
field public static final java.lang.String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
field public static final java.lang.String EXTRA_NOTIFICATION_CHANNEL_GROUP_ID = "android.app.extra.NOTIFICATION_CHANNEL_GROUP_ID";
field public static final java.lang.String EXTRA_NOTIFICATION_CHANNEL_ID = "android.app.extra.NOTIFICATION_CHANNEL_ID";
@@ -5798,6 +5803,8 @@
field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
+ field public static final java.lang.String META_DATA_AUTOMATIC_RULE_TYPE = "android.app.automatic.ruleType";
+ field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.app.zen.automatic.ruleInstanceLimit";
}
public static class NotificationManager.Policy implements android.os.Parcelable {
@@ -11558,18 +11565,19 @@
field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
- field public static final java.lang.String FEATURE_FACE = "android.hardware.face";
+ field public static final java.lang.String FEATURE_FACE = "android.hardware.biometrics.face";
field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
- field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+ field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
+ field public static final java.lang.String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
field public static final java.lang.String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final java.lang.String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
- field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
+ field public static final java.lang.String FEATURE_IRIS = "android.hardware.biometrics.iris";
field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -14854,6 +14862,7 @@
method public int getAmbientShadowColor();
method public int getBottom();
method public float getCameraDistance();
+ method public boolean getClipToBounds();
method public boolean getClipToOutline();
method public float getElevation();
method public int getHeight();
@@ -14874,6 +14883,7 @@
method public float getTranslationY();
method public float getTranslationZ();
method public long getUniqueId();
+ method public boolean getUseCompositingLayer();
method public int getWidth();
method public boolean hasDisplayList();
method public boolean hasIdentityMatrix();
@@ -25301,24 +25311,24 @@
method public java.lang.Object clearNextDataSources();
method public void clearPendingCommands();
method public void close();
- method public java.lang.Object deselectTrack(int);
+ method public java.lang.Object deselectTrack(android.media.DataSourceDesc, int);
method public android.media.AudioAttributes getAudioAttributes();
method public int getAudioSessionId();
- method public long getBufferedPosition();
+ method public long getBufferedPosition(android.media.DataSourceDesc);
method public android.media.DataSourceDesc getCurrentDataSource();
method public long getCurrentPosition();
- method public long getDuration();
+ method public long getDuration(android.media.DataSourceDesc);
method public float getMaxPlayerVolume();
method public android.os.PersistableBundle getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public float getPlayerVolume();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
- method public int getSelectedTrack(int);
+ method public int getSelectedTrack(android.media.DataSourceDesc, int);
method public int getState();
method public android.media.SyncParams getSyncParams();
method public android.media.MediaTimestamp getTimestamp();
- method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
+ method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(android.media.DataSourceDesc);
method public android.media.VideoSize getVideoSize();
method public boolean isLooping();
method public java.lang.Object loopCurrent(boolean);
@@ -25331,7 +25341,7 @@
method public void reset();
method public java.lang.Object seekTo(long);
method public java.lang.Object seekTo(long, int);
- method public java.lang.Object selectTrack(int);
+ method public java.lang.Object selectTrack(android.media.DataSourceDesc, int);
method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
method public java.lang.Object setAudioSessionId(int);
method public java.lang.Object setAuxEffectSendLevel(float);
@@ -41009,10 +41019,10 @@
field public final java.lang.String summary;
}
- public abstract class ConditionProviderService extends android.app.Service {
+ public abstract deprecated class ConditionProviderService extends android.app.Service {
ctor public ConditionProviderService();
- method public final void notifyCondition(android.service.notification.Condition);
- method public final void notifyConditions(android.service.notification.Condition...);
+ method public final deprecated void notifyCondition(android.service.notification.Condition);
+ method public final deprecated void notifyConditions(android.service.notification.Condition...);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract void onConnected();
method public void onRequestConditions(int);
@@ -41020,10 +41030,10 @@
method public abstract void onUnsubscribe(android.net.Uri);
method public static final void requestRebind(android.content.ComponentName);
method public final void requestUnbind();
- field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
- field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
- field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
- field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
+ field public static final deprecated java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
+ field public static final deprecated java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+ field public static final deprecated java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
+ field public static final deprecated java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -41475,6 +41485,7 @@
method protected void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
+ method public android.content.Context getDisplayContext();
method public android.view.SurfaceHolder getSurfaceHolder();
method public boolean isPreview();
method public boolean isVisible();
@@ -46934,7 +46945,7 @@
ctor public deprecated Scene(android.view.ViewGroup, android.view.ViewGroup);
method public void enter();
method public void exit();
- method public static android.transition.Scene getCurrentScene(android.view.View);
+ method public static android.transition.Scene getCurrentScene(android.view.ViewGroup);
method public static android.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
method public android.view.ViewGroup getSceneRoot();
method public void setEnterAction(java.lang.Runnable);
diff --git a/api/system-current.txt b/api/system-current.txt
index a83c32d..8f5ccd1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -205,6 +205,10 @@
field public static final int config_sendPackageName = 17891328; // 0x1110000
}
+ public static final class R.color {
+ field public static final int system_notification_accent_color = 17170460; // 0x106001c
+ }
+
public static final class R.dimen {
field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008
field public static final int config_restrictedIconSize = 17104903; // 0x1050007
@@ -3713,6 +3717,7 @@
method public boolean isWifiScannerSupported();
method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+ method public void setDeviceMobilityState(int);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method public boolean startScan(android.os.WorkSource);
method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -3720,6 +3725,10 @@
field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
field public static final java.lang.String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
+ field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1
+ field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
+ field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
+ field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -5827,6 +5836,7 @@
public class SubscriptionManager {
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method public void requestEmbeddedSubscriptionInfoListRefresh();
+ method public void requestEmbeddedSubscriptionInfoListRefresh(int);
method public void setDefaultDataSubId(int);
method public void setDefaultSmsSubId(int);
field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
diff --git a/api/test-current.txt b/api/test-current.txt
index 627ef22..46e7683 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -655,11 +655,6 @@
method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
}
- public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
- method public android.media.BufferingParams getBufferingParams();
- method public void setBufferingParams(android.media.BufferingParams);
- }
-
public final class PlaybackParams implements android.os.Parcelable {
method public int getAudioStretchMode();
method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -1197,7 +1192,7 @@
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
- public abstract class ConditionProviderService extends android.app.Service {
+ public abstract deprecated class ConditionProviderService extends android.app.Service {
method public boolean isBound();
}
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 834658d..373677e 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -16,13 +16,17 @@
package com.android.commands.bu;
+import android.annotation.UserIdInt;
import android.app.backup.IBackupManager;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.system.OsConstants;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.IOException;
import java.util.ArrayList;
@@ -33,35 +37,50 @@
int mNextArg;
IBackupManager mBackupManager;
+ @VisibleForTesting
+ Backup(IBackupManager backupManager) {
+ mBackupManager = backupManager;
+ }
+
+ Backup() {
+ mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+ }
+
public static void main(String[] args) {
- Log.d(TAG, "Beginning: " + args[0]);
- mArgs = args;
try {
- new Backup().run();
+ new Backup().run(args);
} catch (Exception e) {
Log.e(TAG, "Error running backup/restore", e);
}
Log.d(TAG, "Finished.");
}
- public void run() {
- mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+ public void run(String[] args) {
if (mBackupManager == null) {
Log.e(TAG, "Can't obtain Backup Manager binder");
return;
}
+ Log.d(TAG, "Beginning: " + args[0]);
+ mArgs = args;
+
+ int userId = parseUserId();
+ if (!isBackupActiveForUser(userId)) {
+ Log.e(TAG, "BackupManager is not available for user " + userId);
+ return;
+ }
+
String arg = nextArg();
if (arg.equals("backup")) {
- doBackup(OsConstants.STDOUT_FILENO);
+ doBackup(OsConstants.STDOUT_FILENO, userId);
} else if (arg.equals("restore")) {
- doRestore(OsConstants.STDIN_FILENO);
+ doRestore(OsConstants.STDIN_FILENO, userId);
} else {
showUsage();
}
}
- private void doBackup(int socketFd) {
+ private void doBackup(int socketFd, @UserIdInt int userId) {
ArrayList<String> packages = new ArrayList<String>();
boolean saveApks = false;
boolean saveObbs = false;
@@ -105,6 +124,10 @@
doKeyValue = true;
} else if ("-nokeyvalue".equals(arg)) {
doKeyValue = false;
+ } else if ("-user".equals(arg)) {
+ // User ID has been processed in run(), ignore the next argument.
+ nextArg();
+ continue;
} else {
Log.w(TAG, "Unknown backup flag " + arg);
continue;
@@ -128,7 +151,7 @@
try {
fd = ParcelFileDescriptor.adoptFd(socketFd);
String[] packArray = new String[packages.size()];
- mBackupManager.adbBackup(fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
+ mBackupManager.adbBackup(userId, fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
allIncludesSystem, doCompress, doKeyValue, packages.toArray(packArray));
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke backup manager for backup");
@@ -143,12 +166,12 @@
}
}
- private void doRestore(int socketFd) {
+ private void doRestore(int socketFd, @UserIdInt int userId) {
// No arguments to restore
ParcelFileDescriptor fd = null;
try {
fd = ParcelFileDescriptor.adoptFd(socketFd);
- mBackupManager.adbRestore(fd);
+ mBackupManager.adbRestore(userId, fd);
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke backup manager for restore");
} finally {
@@ -160,11 +183,31 @@
}
}
+ private @UserIdInt int parseUserId() {
+ for (int argNumber = 0; argNumber < mArgs.length - 1; argNumber++) {
+ if ("-user".equals(mArgs[argNumber])) {
+ return UserHandle.parseUserArg(mArgs[argNumber + 1]);
+ }
+ }
+
+ return UserHandle.USER_SYSTEM;
+ }
+
+ private boolean isBackupActiveForUser(int userId) {
+ try {
+ return mBackupManager.isBackupServiceActive(userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not access BackupManager: " + e.toString());
+ return false;
+ }
+ }
+
private static void showUsage() {
- System.err.println(" backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all]");
- System.err.println(" [-system|-nosystem] [-keyvalue|-nokeyvalue] [PACKAGE...]");
+ System.err.println(" backup [-user USER_ID] [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared]");
+ System.err.println(" [-all] [-system|-nosystem] [-keyvalue|-nokeyvalue] [PACKAGE...]");
System.err.println(" write an archive of the device's data to FILE [default=backup.adb]");
System.err.println(" package list optional if -all/-shared are supplied");
+ System.err.println(" -user: user ID for which to perform the operation (default - system user)");
System.err.println(" -apk/-noapk: do/don't back up .apk files (default -noapk)");
System.err.println(" -obb/-noobb: do/don't back up .obb files (default -noobb)");
System.err.println(" -shared|-noshared: do/don't back up shared storage (default -noshared)");
@@ -172,7 +215,8 @@
System.err.println(" -system|-nosystem: include system apps in -all (default -system)");
System.err.println(" -keyvalue|-nokeyvalue: include apps that perform key/value backups.");
System.err.println(" (default -nokeyvalue)");
- System.err.println(" restore FILE restore device contents from FILE");
+ System.err.println(" restore [-user USER_ID] FILE restore device contents from FILE");
+ System.err.println(" -user: user ID for which to perform the operation (default - system user)");
}
private String nextArg() {
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 020c443..8d0cee5 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -36,6 +36,7 @@
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
#include "idmap2/Xml.h"
#include "idmap2/ZipFile.h"
@@ -53,39 +54,40 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::IdmapHeader;
using android::idmap2::ResourceId;
+using android::idmap2::Result;
using android::idmap2::Xml;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace {
-std::pair<bool, ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am,
- const std::string& res,
- const std::string& fallback_package) {
+
+Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res,
+ const std::string& fallback_package) {
// first, try to parse as a hex number
char* endptr = nullptr;
ResourceId resid;
resid = strtol(res.c_str(), &endptr, 16);
if (*endptr == '\0') {
- return std::make_pair(true, resid);
+ return {resid};
}
// next, try to parse as a package:type/name string
resid = am.GetResourceId(res, "", fallback_package);
if (is_valid_resid(resid)) {
- return std::make_pair(true, resid);
+ return {resid};
}
// end of the road: res could not be parsed
- return std::make_pair(false, 0);
+ return {};
}
-std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
Res_value value;
ResTable_config config;
uint32_t flags;
ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
if (cookie == kInvalidCookie) {
- return std::make_pair(false, "");
+ return {};
}
std::string out;
@@ -123,31 +125,31 @@
out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
break;
}
- return std::make_pair(true, out);
+ return {out};
}
-std::pair<bool, std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
+Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
const auto zip = ZipFile::Open(apk_path);
if (!zip) {
- return std::make_pair(false, "");
+ return {};
}
const auto entry = zip->Uncompress("AndroidManifest.xml");
if (!entry) {
- return std::make_pair(false, "");
+ return {};
}
const auto xml = Xml::Create(entry->buf, entry->size);
if (!xml) {
- return std::make_pair(false, "");
+ return {};
}
const auto tag = xml->FindTag("overlay");
if (!tag) {
- return std::make_pair(false, "");
+ return {};
}
const auto iter = tag->find("targetPackage");
if (iter == tag->end()) {
- return std::make_pair(false, "");
+ return {};
}
- return std::make_pair(true, iter->second);
+ return {iter->second};
}
} // namespace
@@ -195,14 +197,14 @@
}
apk_assets.push_back(std::move(target_apk));
- bool lookup_ok;
- std::tie(lookup_ok, target_package_name) =
+ const Result<std::string> package_name =
GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
- if (!lookup_ok) {
+ if (!package_name) {
out_error << "error: failed to parse android:targetPackage from overlay manifest"
<< std::endl;
return false;
}
+ target_package_name = *package_name;
} else if (target_path != idmap_header->GetTargetPath()) {
out_error << "error: different target APKs (expected target APK " << target_path << " but "
<< idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
@@ -227,21 +229,18 @@
am.SetApkAssets(raw_pointer_apk_assets);
am.SetConfiguration(config);
- ResourceId resid;
- bool lookup_ok;
- std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name);
- if (!lookup_ok) {
+ const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
+ if (!resid) {
out_error << "error: failed to parse resource ID" << std::endl;
return false;
}
- std::string value;
- std::tie(lookup_ok, value) = GetValue(am, resid);
- if (!lookup_ok) {
- out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl;
+ const Result<std::string> value = GetValue(am, *resid);
+ if (!value) {
+ out_error << StringPrintf("error: resource 0x%08x not found", *resid) << std::endl;
return false;
}
- std::cout << value << std::endl;
+ std::cout << *value << std::endl;
return true;
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index cf72cb9..86b00f1 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -78,6 +78,18 @@
}
}
+Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
+ int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+ assert(_aidl_return);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ std::ifstream fin(idmap_path);
+ const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+ fin.close();
+ std::stringstream dev_null;
+ *_aidl_return = header && header->IsUpToDate(dev_null);
+ return ok();
+}
+
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return) {
@@ -90,17 +102,6 @@
_aidl_return->reset(nullptr);
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
- std::ifstream fin(idmap_path);
- const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
- fin.close();
- // do not reuse error stream from IsUpToDate below, or error messages will be
- // polluted with irrelevant data
- std::stringstream dev_null;
- if (header && header->IsUpToDate(dev_null)) {
- return ok();
- }
-
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return error("failed to load apk " + target_apk_path);
@@ -119,6 +120,7 @@
}
umask(0133); // u=rw,g=r,o=r
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return error("failed to open idmap path " + idmap_path);
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 2b32042..4e5abc9 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -39,6 +39,9 @@
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
bool* _aidl_return);
+ binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id,
+ bool* _aidl_return);
+
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return);
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index 5d19610..d475417 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -22,6 +22,7 @@
interface IIdmap2 {
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
+ boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath, int userId);
}
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 88a835b..d106f19 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -18,19 +18,18 @@
#define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "androidfw/AssetManager2.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
namespace utils {
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
- ResourceId resid);
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
} // namespace utils
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h
new file mode 100644
index 0000000..6189ea3
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Result.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+
+#include <optional>
+
+namespace android::idmap2 {
+
+template <typename T>
+using Result = std::optional<T>;
+
+static constexpr std::nullopt_t kResultError = std::nullopt;
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESULT_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
index 328bd36..9edbbe0 100644
--- a/cmds/idmap2/include/idmap2/ZipFile.h
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -19,10 +19,10 @@
#include <memory>
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "ziparchive/zip_archive.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
@@ -43,7 +43,7 @@
static std::unique_ptr<const ZipFile> Open(const std::string& path);
std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
- std::pair<bool, uint32_t> Crc(const std::string& entryPath) const;
+ Result<uint32_t> Crc(const std::string& entryPath) const;
~ZipFile();
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 5a47e30..1ef3267 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -33,6 +33,7 @@
#include "idmap2/Idmap.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
namespace android {
@@ -143,18 +144,16 @@
return false;
}
- bool status;
- uint32_t target_crc;
- std::tie(status, target_crc) = target_zip->Crc("resources.arsc");
- if (!status) {
+ Result<uint32_t> target_crc = target_zip->Crc("resources.arsc");
+ if (!target_crc) {
out_error << "error: failed to get target crc" << std::endl;
return false;
}
- if (target_crc_ != target_crc) {
+ if (target_crc_ != *target_crc) {
out_error << base::StringPrintf(
"error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
- target_crc_, target_crc)
+ target_crc_, *target_crc)
<< std::endl;
return false;
}
@@ -165,17 +164,16 @@
return false;
}
- uint32_t overlay_crc;
- std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc");
- if (!status) {
+ Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc");
+ if (!overlay_crc) {
out_error << "error: failed to get overlay crc" << std::endl;
return false;
}
- if (overlay_crc_ != overlay_crc) {
+ if (overlay_crc_ != *overlay_crc) {
out_error << base::StringPrintf(
"error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
- overlay_crc_, overlay_crc)
+ overlay_crc_, *overlay_crc)
<< std::endl;
return false;
}
@@ -322,17 +320,20 @@
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
- bool crc_status;
- std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc");
- if (!crc_status) {
+
+ Result<uint32_t> crc = target_zip->Crc("resources.arsc");
+ if (!crc) {
out_error << "error: failed to get zip crc for target" << std::endl;
return nullptr;
}
- std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc");
- if (!crc_status) {
+ header->target_crc_ = *crc;
+
+ crc = overlay_zip->Crc("resources.arsc");
+ if (!crc) {
out_error << "error: failed to get zip crc for overlay" << std::endl;
return nullptr;
}
+ header->overlay_crc_ = *crc;
if (target_apk_path.size() > sizeof(header->target_path_)) {
out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
@@ -358,15 +359,14 @@
const auto end = overlay_pkg->end();
for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
const ResourceId overlay_resid = *iter;
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
- if (!lookup_ok) {
+ Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
+ if (!name) {
continue;
}
// prepend "<package>:" to turn name into "<package>:<type>/<name>"
- name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str());
- const ResourceId target_resid = NameToResid(target_asset_manager, name);
+ const std::string full_name =
+ base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
+ const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
if (target_resid == 0) {
continue;
}
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 492e6f0..fb3bc5b 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -15,7 +15,6 @@
*/
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
@@ -23,6 +22,7 @@
#include "idmap2/PrettyPrintVisitor.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
@@ -63,11 +63,9 @@
stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
if (target_package_loaded) {
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
- if (lookup_ok) {
- stream_ << " " << name;
+ Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+ if (name) {
+ stream_ << " " << *name;
}
}
stream_ << std::endl;
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 57cfc8e..7c24445 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -16,7 +16,6 @@
#include <cstdarg>
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
@@ -24,6 +23,7 @@
#include "idmap2/RawPrintVisitor.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
using android::ApkAssets;
@@ -75,14 +75,13 @@
const ResourceId target_resid =
RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
- bool lookup_ok = false;
- std::string name;
+ Result<std::string> name;
if (target_package_loaded) {
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+ name = utils::ResToTypeEntryName(target_am_, target_resid);
}
- if (lookup_ok) {
+ if (name) {
print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
- name.c_str());
+ name->c_str());
} else {
print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
}
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index e98f843..5c89783 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -15,12 +15,12 @@
*/
#include <string>
-#include <utility>
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
using android::StringPiece16;
using android::util::Utf16ToUtf8;
@@ -29,11 +29,10 @@
namespace idmap2 {
namespace utils {
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
- ResourceId resid) {
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
AssetManager2::ResourceName name;
if (!am.GetResourceName(resid, &name)) {
- return std::make_pair(false, "");
+ return {};
}
std::string out;
if (name.type != nullptr) {
@@ -47,7 +46,7 @@
} else {
out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
}
- return std::make_pair(true, out);
+ return {out};
}
} // namespace utils
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 3f2079a..9fb611d 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -16,8 +16,8 @@
#include <memory>
#include <string>
-#include <utility>
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
namespace android {
@@ -57,10 +57,10 @@
return chunk;
}
-std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const {
+Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
::ZipEntry entry;
int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
- return std::make_pair(status == 0, entry.crc32);
+ return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError;
}
} // namespace idmap2
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 0547fa0..7f60d75 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -16,13 +16,13 @@
#include <memory>
#include <string>
-#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "androidfw/ApkAssets.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
#include "TestHelpers.h"
@@ -52,17 +52,14 @@
};
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
- ASSERT_TRUE(lookup_ok);
- ASSERT_EQ(name, "integer/int1");
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+ ASSERT_TRUE(name);
+ ASSERT_EQ(*name, "integer/int1");
}
TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
- bool lookup_ok;
- std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
- ASSERT_FALSE(lookup_ok);
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+ ASSERT_FALSE(name);
}
} // namespace idmap2
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
index a504d31..6e4a501 100644
--- a/cmds/idmap2/tests/ZipFileTests.cpp
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -16,8 +16,8 @@
#include <cstdio> // fclose
#include <string>
-#include <utility>
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
#include "gmock/gmock.h"
@@ -44,14 +44,12 @@
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
ASSERT_THAT(zip, NotNull());
- bool status;
- uint32_t crc;
- std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
- ASSERT_TRUE(status);
- ASSERT_EQ(crc, 0x762f3d24);
+ Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
+ ASSERT_TRUE(crc);
+ ASSERT_EQ(*crc, 0x762f3d24);
- std::tie(status, std::ignore) = zip->Crc("does-not-exist");
- ASSERT_FALSE(status);
+ Result<uint32_t> crc2 = zip->Crc("does-not-exist");
+ ASSERT_FALSE(crc2);
}
TEST(ZipFileTests, Uncompress) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5df47cd..5899e0cf 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -874,6 +874,7 @@
/**
* Logs when Wifi is toggled on/off.
+ * Note that Wifi may still perform certain functions (e.g. location scanning) even when disabled.
*
* Logged from:
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -888,7 +889,8 @@
/**
* Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode.
- * TODO: Include support for Hotspot.
+ * TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'.
+ * Note that Wifi Scanning is monitored separately in WifiScanStateChanged.
*
* Logged from:
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 16b7e79..5fea90b 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -106,14 +106,14 @@
// Add to set.
mConfigs[key.GetUid()].insert(key);
- for (sp<ConfigListener> listener : mListeners) {
+ for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
const int64_t timestampNs = getElapsedRealtimeNs();
// Tell everyone
- for (sp<ConfigListener> listener : broadcastList) {
+ for (const sp<ConfigListener>& listener : broadcastList) {
listener->OnConfigUpdated(timestampNs, key, config);
}
}
@@ -137,7 +137,7 @@
if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) {
// Remove from map
uidIt->second.erase(key);
- for (sp<ConfigListener> listener : mListeners) {
+ for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
@@ -153,7 +153,7 @@
remove_saved_configs(key);
}
- for (sp<ConfigListener> listener:broadcastList) {
+ for (const sp<ConfigListener>& listener:broadcastList) {
listener->OnConfigRemoved(key);
}
}
@@ -183,7 +183,7 @@
mConfigs.erase(uidIt);
- for (sp<ConfigListener> listener : mListeners) {
+ for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
@@ -191,7 +191,7 @@
// Remove separately so if they do anything in the callback they can't mess up our iteration.
for (auto& key : removed) {
// Tell everyone
- for (sp<ConfigListener> listener:broadcastList) {
+ for (const sp<ConfigListener>& listener:broadcastList) {
listener->OnConfigRemoved(key);
}
}
@@ -213,7 +213,7 @@
}
mConfigReceivers.clear();
- for (sp<ConfigListener> listener : mListeners) {
+ for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
@@ -221,7 +221,7 @@
// Remove separately so if they do anything in the callback they can't mess up our iteration.
for (auto& key : removed) {
// Tell everyone
- for (sp<ConfigListener> listener:broadcastList) {
+ for (const sp<ConfigListener>& listener:broadcastList) {
listener->OnConfigRemoved(key);
}
}
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index c8c3920..d822959 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -281,7 +281,7 @@
(long long)state.residencyInMsecSinceBoot,
(long long)state.totalTransitions,
state.supportedOnlyInSuspend ? 1 : 0);
- for (auto voter : state.voters) {
+ for (const auto& voter : state.voters) {
auto voterPtr = make_shared<LogEvent>(
android::util::SUBSYSTEM_SLEEP_STATE,
wallClockTimestampNs, elapsedTimestampNs);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index ac34f47..dd969c0 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -412,7 +412,7 @@
// Returns the total byte size of all metrics managed by a single config source.
size_t MetricsManager::byteSize() {
size_t totalSize = 0;
- for (auto metricProducer : mAllMetricProducers) {
+ for (const auto& metricProducer : mAllMetricProducers) {
totalSize += metricProducer->byteSize();
}
return totalSize;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index b317361..180a1ae 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -598,7 +598,7 @@
}
noReportMetricIds.insert(no_report_metric);
}
- for (auto it : allMetricProducers) {
+ for (const auto& it : allMetricProducers) {
uidMap.addListener(it);
}
return true;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 59f3f04..e9c43cd 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -152,7 +152,7 @@
// listener removes itself before we call it. It's then the listener's job to handle it (expect
// the callback to be called after listener is removed, and the listener should properly
// ignore it).
- for (auto weakPtr : broadcastList) {
+ for (const auto& weakPtr : broadcastList) {
auto strongPtr = weakPtr.promote();
if (strongPtr != NULL) {
strongPtr->onUidMapReceived(timestamp);
@@ -200,7 +200,7 @@
StatsdStats::getInstance().setUidMapChanges(mChanges.size());
}
- for (auto weakPtr : broadcastList) {
+ for (const auto& weakPtr : broadcastList) {
auto strongPtr = weakPtr.promote();
if (strongPtr != NULL) {
strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
@@ -269,7 +269,7 @@
getListenerListCopyLocked(&broadcastList);
}
- for (auto weakPtr : broadcastList) {
+ for (const auto& weakPtr : broadcastList) {
auto strongPtr = weakPtr.promote();
if (strongPtr != NULL) {
strongPtr->notifyAppRemoved(timestamp, app, uid);
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 79bed52..960fbda 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -71,12 +71,12 @@
const std::shared_ptr<DimToValMap>& currentBucket,
const std::set<const MetricDimensionKey>& trueList,
const std::set<const MetricDimensionKey>& falseList) {
- for (MetricDimensionKey key : trueList) {
+ for (const MetricDimensionKey& key : trueList) {
if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
}
- for (MetricDimensionKey key : falseList) {
+ for (const MetricDimensionKey& key : falseList) {
if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2b81c86..17529a6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -794,12 +794,25 @@
@Override
public List<ModuleInfo> getInstalledModules(int flags) {
- return null;
+ try {
+ return mPM.getInstalledModules(flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@Override
public ModuleInfo getModuleInfo(String packageName, int flags) throws NameNotFoundException {
- return null;
+ try {
+ ModuleInfo mi = mPM.getModuleInfo(packageName, flags);
+ if (mi != null) {
+ return mi;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ throw new NameNotFoundException("No module info for package: " + packageName);
}
@SuppressWarnings("unchecked")
@@ -3002,7 +3015,6 @@
}
}
- @Override
public void sendDeviceCustomizationReadyBroadcast() {
try {
mPM.sendDeviceCustomizationReadyBroadcast();
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index e2f2075..fe23e21 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -20,10 +20,14 @@
import android.app.NotificationManager.InterruptionFilter;
import android.content.ComponentName;
+import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.notification.ZenPolicy;
+import android.service.notification.Condition;
+
+import com.android.internal.util.Preconditions;
import java.util.Objects;
@@ -40,6 +44,7 @@
private @InterruptionFilter int interruptionFilter;
private Uri conditionId;
private ComponentName owner;
+ private ComponentName configurationActivity;
private long creationTime;
private ZenPolicy mZenPolicy;
private boolean mModified = false;
@@ -49,39 +54,51 @@
*
* @param name The name of the rule.
* @param owner The Condition Provider service that owns this rule.
- * @param conditionId A representation of the state that should cause the Condition Provider
- * service to apply the given interruption filter.
* @param interruptionFilter The interruption filter defines which notifications are allowed to
* interrupt the user (e.g. via sound & vibration) while this rule
* is active.
* @param enabled Whether the rule is enabled.
+ * @deprecated use {@link #AutomaticZenRule(String, ComponentName, ComponentName, Uri,
+ * ZenPolicy, int, boolean)}.
*/
+ @Deprecated
public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
int interruptionFilter, boolean enabled) {
- this.name = name;
- this.owner = owner;
- this.conditionId = conditionId;
- this.interruptionFilter = interruptionFilter;
- this.enabled = enabled;
+ this(name, owner, null, conditionId, null, interruptionFilter, enabled);
}
/**
* Creates an automatic zen rule.
*
* @param name The name of the rule.
- * @param owner The Condition Provider service that owns this rule.
- * @param conditionId A representation of the state that should cause the Condition Provider
- * service to apply the given interruption filter.
+ * @param owner The Condition Provider service that owns this rule. This can be null if you're
+ * using {@link NotificationManager#setAutomaticZenRuleState(String, Condition)}
+ * instead of {@link android.service.notification.ConditionProviderService}.
+ * @param configurationActivity An activity that handles
+ * {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows
+ * the user
+ * more information about this rule and/or allows them to
+ * configure it. This is required if you are not using a
+ * {@link android.service.notification.ConditionProviderService}.
+ * If you are, it overrides the information specified in your
+ * manifest.
+ * @param conditionId A representation of the state that should cause your app to apply the
+ * given interruption filter.
+ * @param interruptionFilter The interruption filter defines which notifications are allowed to
+ * interrupt the user (e.g. via sound & vibration) while this rule
+ * is active.
* @param policy The policy defines which notifications are allowed to interrupt the user
- * while this rule is active
+ * while this rule is active. This overrides the global policy while this rule is
+ * action ({@link Condition#STATE_TRUE}).
* @param enabled Whether the rule is enabled.
*/
- public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy,
- boolean enabled) {
+ public AutomaticZenRule(String name, ComponentName owner, ComponentName configurationActivity,
+ Uri conditionId, ZenPolicy policy, int interruptionFilter, boolean enabled) {
this.name = name;
this.owner = owner;
+ this.configurationActivity = configurationActivity;
this.conditionId = conditionId;
- this.interruptionFilter = INTERRUPTION_FILTER_PRIORITY;
+ this.interruptionFilter = interruptionFilter;
this.enabled = enabled;
this.mZenPolicy = policy;
}
@@ -89,18 +106,10 @@
/**
* @hide
*/
- public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
- int interruptionFilter, boolean enabled, long creationTime) {
- this(name, owner, conditionId, interruptionFilter, enabled);
- this.creationTime = creationTime;
- }
-
- /**
- * @hide
- */
- public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy,
- boolean enabled, long creationTime) {
- this(name, owner, conditionId, policy, enabled);
+ public AutomaticZenRule(String name, ComponentName owner, ComponentName configurationActivity,
+ Uri conditionId, ZenPolicy policy, int interruptionFilter, boolean enabled,
+ long creationTime) {
+ this(name, owner, configurationActivity, conditionId, policy, interruptionFilter, enabled);
this.creationTime = creationTime;
}
@@ -112,6 +121,7 @@
interruptionFilter = source.readInt();
conditionId = source.readParcelable(null);
owner = source.readParcelable(null);
+ configurationActivity = source.readParcelable(null);
creationTime = source.readLong();
mZenPolicy = source.readParcelable(null);
mModified = source.readInt() == ENABLED;
@@ -125,6 +135,14 @@
}
/**
+ * Returns the {@link ComponentName} of the activity that shows configuration options
+ * for this rule.
+ */
+ public ComponentName getConfigurationActivity() {
+ return configurationActivity;
+ }
+
+ /**
* Returns the representation of the state that causes this rule to become active.
*/
public Uri getConditionId() {
@@ -218,6 +236,15 @@
this.mZenPolicy = zenPolicy;
}
+ /**
+ * Sets the configuration activity - an activity that handles
+ * {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows the user more information
+ * about this rule and/or allows them to configure it.
+ */
+ public void setConfigurationActivity(ComponentName componentName) {
+ this.configurationActivity = componentName;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -235,6 +262,7 @@
dest.writeInt(interruptionFilter);
dest.writeParcelable(conditionId, 0);
dest.writeParcelable(owner, 0);
+ dest.writeParcelable(configurationActivity, 0);
dest.writeLong(creationTime);
dest.writeParcelable(mZenPolicy, 0);
dest.writeInt(mModified ? ENABLED : DISABLED);
@@ -248,6 +276,7 @@
.append(",interruptionFilter=").append(interruptionFilter)
.append(",conditionId=").append(conditionId)
.append(",owner=").append(owner)
+ .append(",configActivity=").append(configurationActivity)
.append(",creationTime=").append(creationTime)
.append(",mZenPolicy=").append(mZenPolicy)
.append(']').toString();
@@ -264,14 +293,15 @@
&& other.interruptionFilter == interruptionFilter
&& Objects.equals(other.conditionId, conditionId)
&& Objects.equals(other.owner, owner)
- && other.creationTime == creationTime
- && Objects.equals(other.mZenPolicy, mZenPolicy);
+ && Objects.equals(other.mZenPolicy, mZenPolicy)
+ && Objects.equals(other.configurationActivity, configurationActivity)
+ && other.creationTime == creationTime;
}
@Override
public int hashCode() {
- return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime,
- mZenPolicy, mModified);
+ return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
+ configurationActivity, mZenPolicy, mModified, creationTime);
}
public static final Parcelable.Creator<AutomaticZenRule> CREATOR
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e508d42..00567523 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -164,6 +164,7 @@
boolean removeAutomaticZenRule(String id);
boolean removeAutomaticZenRules(String packageName);
int getRuleInstanceCount(in ComponentName owner);
+ void setAutomaticZenRuleState(String id, in Condition condition);
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 25fa897..306c366 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -41,6 +41,7 @@
import android.os.StrictMode;
import android.os.UserHandle;
import android.provider.Settings.Global;
+import android.service.notification.Condition;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
import android.util.Log;
@@ -262,6 +263,68 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Importance {}
+ /**
+ * Activity Action: Launch an Automatic Zen Rule configuration screen
+ * <p>
+ * Input: Optionally, {@link #EXTRA_AUTOMATIC_RULE_ID}, if the configuration screen for an
+ * existing rule should be displayed. If the rule id is missing or null, apps should display
+ * a configuration screen where users can create a new instance of the rule.
+ * <p>
+ * Output: Nothing
+ * <p>
+ * You can have multiple activities handling this intent, if you support multiple
+ * {@link AutomaticZenRule rules}. In order for the system to properly display all of your
+ * rule types so that users can create new instances or configure existing ones, you need
+ * to add some extra metadata ({@link #META_DATA_AUTOMATIC_RULE_TYPE})
+ * to your activity tag in your manifest. If you'd like to limit the number of rules a user
+ * can create from this flow, you can additionally optionally include
+ * {@link #META_DATA_RULE_INSTANCE_LIMIT}.
+ *
+ * For example,
+ * <meta-data
+ * android:name="android.app.zen.automatic.ruleType"
+ * android:value="@string/my_condition_rule">
+ * </meta-data>
+ * <meta-data
+ * android:name="android.app.zen.automatic.ruleInstanceLimit"
+ * android:value="1">
+ * </meta-data>
+ * </p>
+ * </p>
+ *
+ * @see {@link #addAutomaticZenRule(AutomaticZenRule)}
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_AUTOMATIC_ZEN_RULE =
+ "android.app.action.AUTOMATIC_ZEN_RULE";
+
+ /**
+ * Used as an optional string extra on {@link #ACTION_AUTOMATIC_ZEN_RULE} intents. If
+ * provided, contains the id of the {@link AutomaticZenRule} (as returned from
+ * {@link NotificationManager#addAutomaticZenRule(AutomaticZenRule)}) for which configuration
+ * settings should be displayed.
+ */
+ public static final String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
+
+ /**
+ * A required {@code meta-data} tag for activities that handle
+ * {@link #ACTION_AUTOMATIC_ZEN_RULE}.
+ *
+ * This tag should contain a localized name of the type of the zen rule provided by the
+ * activity.
+ */
+ public static final String META_DATA_AUTOMATIC_RULE_TYPE = "android.app.automatic.ruleType";
+
+ /**
+ * An optional {@code meta-data} tag for activities that handle
+ * {@link #ACTION_AUTOMATIC_ZEN_RULE}.
+ *
+ * This tag should contain the maximum number of rule instances that
+ * can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances.
+ */
+ public static final String META_DATA_RULE_INSTANCE_LIMIT =
+ "android.app.zen.automatic.ruleInstanceLimit";
+
/** Value signifying that the user has not expressed a per-app visibility override value.
* @hide */
public static final int VISIBILITY_NO_OVERRIDE = -1000;
@@ -859,14 +922,10 @@
List<ZenModeConfig.ZenRule> rules = service.getZenRules();
Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
for (ZenModeConfig.ZenRule rule : rules) {
- if (rule.zenPolicy == null) {
- ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
- rule.conditionId, zenModeToInterruptionFilter(rule.zenMode),
- rule.enabled, rule.creationTime));
- } else {
- ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
- rule.conditionId, rule.zenPolicy, rule.enabled, rule.creationTime));
- }
+ ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
+ rule.configurationActivity, rule.conditionId, rule.zenPolicy,
+ zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
+ rule.creationTime));
}
return ruleMap;
} catch (RemoteException e) {
@@ -936,6 +995,26 @@
}
/**
+ * Informs the notification manager that the state of an {@link AutomaticZenRule} has changed.
+ * Use this method to put the system into Do Not Disturb mode or request that it exits Do Not
+ * Disturb mode. The calling app must own the provided {@link android.app.AutomaticZenRule}.
+ * <p>
+ * This method can be used in conjunction with or as a replacement to
+ * {@link android.service.notification.ConditionProviderService#notifyCondition(Condition)}.
+ * </p>
+ * @param id The id of the rule whose state should change
+ * @param condition The new state of this rule
+ */
+ public void setAutomaticZenRuleState(String id, Condition condition) {
+ INotificationManager service = getService();
+ try {
+ service.setAutomaticZenRuleState(id, condition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Deletes the automatic zen rule with the given id.
*
* <p>
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 0afb98f..f1e6b06 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -189,8 +189,11 @@
* completed.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If the {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
- * @param fd The file descriptor to which a 'tar' file stream is to be written
+ * @param userId User id for which backup should be performed.
+ * @param fd The file descriptor to which a 'tar' file stream is to be written.
* @param includeApks If <code>true</code>, the resulting tar stream will include the
* application .apk files themselves as well as their data.
* @param includeObbs If <code>true</code>, the resulting tar stream will include any
@@ -209,7 +212,7 @@
* @param packageNames The package names of the apps whose data (and optionally .apk files)
* are to be backed up. The <code>allApps</code> parameter supersedes this.
*/
- void adbBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+ void adbBackup(int userId, in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem,
boolean doCompress, boolean doKeyValue, in String[] packageNames);
@@ -227,8 +230,12 @@
* Currently only used by the 'adb restore' command.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If the {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL.
+ *
+ * @param userId User id for which restore should be performed.
*/
- void adbRestore(in ParcelFileDescriptor fd);
+ void adbRestore(int userId, in ParcelFileDescriptor fd);
/**
* Confirm that the requested full backup/restore operation can proceed. The system will
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index eea2b88..a4ea513 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -36,6 +36,7 @@
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
@@ -680,4 +681,8 @@
boolean isPackageStateProtected(String packageName, int userId);
void sendDeviceCustomizationReadyBroadcast();
+
+ List<ModuleInfo> getInstalledModules(int flags);
+
+ ModuleInfo getModuleInfo(String packageName, int flags);
}
diff --git a/core/java/android/content/pm/ModuleInfo.aidl b/core/java/android/content/pm/ModuleInfo.aidl
new file mode 100644
index 0000000..cc13bf1
--- /dev/null
+++ b/core/java/android/content/pm/ModuleInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.content.pm;
+
+parcelable ModuleInfo;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 566017b..9d604bb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2288,21 +2288,28 @@
* {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+ public static final String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_FACE = "android.hardware.face";
+ public static final String FEATURE_FACE = "android.hardware.biometrics.face";
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_IRIS = "android.hardware.iris";
+ public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
/**
* Feature for {@link #getSystemAvailableFeatures} and
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 4b1a08d..0877a1a4 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -45,6 +45,20 @@
in String[] ipAddresses, int ipAddressesCount, long timestamp, int uid);
/**
+ * Represents adding or removing a NAT64 prefix.
+ * This method must not block or perform long-running operations.
+ *
+ * @param netId the ID of the network the prefix was performed on.
+ * @param added true if the NAT64 prefix was added, or false if the NAT64 prefix was removed.
+ * There is only one prefix at a time for each netId. If a prefix is added, it replaces
+ * the previous-added prefix.
+ * @param prefixString the detected NAT64 prefix as a string literal.
+ * @param prefixLength the prefix length associated with this NAT64 prefix.
+ */
+ void onNat64PrefixEvent(int netId, boolean added, @utf8InCpp String prefixString,
+ int prefixLength);
+
+ /**
* Represents a private DNS validation success or failure.
* This method must not block or perform long-running operations.
*
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 428d9e1..e84a518 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -23,6 +23,7 @@
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
import static android.system.OsConstants.F_OK;
+import static android.system.OsConstants.O_ACCMODE;
import static android.system.OsConstants.O_APPEND;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
@@ -1259,11 +1260,11 @@
int res = 0;
if (mode.startsWith("rw")) {
- res |= O_RDWR | O_CREAT;
+ res = O_RDWR | O_CREAT;
} else if (mode.startsWith("w")) {
- res |= O_WRONLY | O_CREAT;
+ res = O_WRONLY | O_CREAT;
} else if (mode.startsWith("r")) {
- res |= O_RDONLY;
+ res = O_RDONLY;
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1279,12 +1280,12 @@
/** {@hide} */
public static String translateModePosixToString(int mode) {
String res = "";
- if ((mode & O_RDWR) == O_RDWR) {
- res += "rw";
- } else if ((mode & O_WRONLY) == O_WRONLY) {
- res += "w";
- } else if ((mode & O_RDONLY) == O_RDONLY) {
- res += "r";
+ if ((mode & O_ACCMODE) == O_RDWR) {
+ res = "rw";
+ } else if ((mode & O_ACCMODE) == O_WRONLY) {
+ res = "w";
+ } else if ((mode & O_ACCMODE) == O_RDONLY) {
+ res = "r";
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1300,12 +1301,12 @@
/** {@hide} */
public static int translateModePosixToPfd(int mode) {
int res = 0;
- if ((mode & O_RDWR) == O_RDWR) {
- res |= MODE_READ_WRITE;
- } else if ((mode & O_WRONLY) == O_WRONLY) {
- res |= MODE_WRITE_ONLY;
- } else if ((mode & O_RDONLY) == O_RDONLY) {
- res |= MODE_READ_ONLY;
+ if ((mode & O_ACCMODE) == O_RDWR) {
+ res = MODE_READ_WRITE;
+ } else if ((mode & O_ACCMODE) == O_WRONLY) {
+ res = MODE_WRITE_ONLY;
+ } else if ((mode & O_ACCMODE) == O_RDONLY) {
+ res = MODE_READ_ONLY;
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1325,11 +1326,11 @@
public static int translateModePfdToPosix(int mode) {
int res = 0;
if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
- res |= O_RDWR;
+ res = O_RDWR;
} else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
- res |= O_WRONLY;
+ res = O_WRONLY;
} else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
- res |= O_RDONLY;
+ res = O_RDONLY;
} else {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -1428,4 +1429,3 @@
}
}
}
-
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index af7e93e..30d9804 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -29,7 +29,7 @@
/**
* The current condition of an {@link android.app.AutomaticZenRule}, provided by the
- * {@link ConditionProviderService} that owns the rule. Used to tell the system to enter Do Not
+ * app that owns the rule. Used to tell the system to enter Do Not
* Disturb mode and request that the system exit Do Not Disturb mode.
*/
public final class Condition implements Parcelable {
@@ -48,8 +48,8 @@
/**
* Indicates that Do Not Disturb should be turned off. Note that all Conditions from all
- * {@link ConditionProviderService} providers must be off for Do Not Disturb to be turned off on
- * the device.
+ * {@link android.app.AutomaticZenRule} providers must be off for Do Not Disturb to be turned
+ * off on the device.
*/
public static final int STATE_FALSE = 0;
/**
@@ -154,7 +154,7 @@
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- // id is guarantreed not to be null.
+ // id is guaranteed not to be null.
proto.write(ConditionProto.ID, id.toString());
proto.write(ConditionProto.SUMMARY, summary);
proto.write(ConditionProto.LINE_1, line1);
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 5203c8f..45480cb 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -59,7 +59,16 @@
*
* <p> Condition providers cannot be bound by the system on
* {@link ActivityManager#isLowRamDevice() low ram} devices</p>
+ *
+ * @deprecated Instead of using an automatically bound service, use
+ * {@link android.app.NotificationManager#setAutomaticZenRuleState(String, Condition)} to tell the
+ * system about the state of your rule. In order to maintain a link from
+ * Settings to your rule configuration screens, provide a configuration activity that handles
+ * {@link android.app.NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} on your
+ * {@link android.app.AutomaticZenRule} via
+ * {@link android.app.AutomaticZenRule#setConfigurationActivity(ComponentName)}.
*/
+@Deprecated
public abstract class ConditionProviderService extends Service {
private final String TAG = ConditionProviderService.class.getSimpleName()
+ "[" + getClass().getSimpleName() + "]";
@@ -79,26 +88,38 @@
/**
* The name of the {@code meta-data} tag containing a localized name of the type of zen rules
* provided by this service.
+ *
+ * @deprecated see {@link android.app.NotificationManager#META_DATA_AUTOMATIC_RULE_TYPE}.
*/
+ @Deprecated
public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
/**
* The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
* that allows users to configure the conditions provided by this service.
+ *
+ * @deprecated see {@link android.app.NotificationManager#ACTION_AUTOMATIC_ZEN_RULE}.
*/
+ @Deprecated
public static final String META_DATA_CONFIGURATION_ACTIVITY =
"android.service.zen.automatic.configurationActivity";
/**
* The name of the {@code meta-data} tag containing the maximum number of rule instances that
* can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances.
+ *
+ * @deprecated see {@link android.app.NotificationManager#META_DATA_RULE_INSTANCE_LIMIT}.
*/
+ @Deprecated
public static final String META_DATA_RULE_INSTANCE_LIMIT =
"android.service.zen.automatic.ruleInstanceLimit";
/**
* A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
+ *
+ * @deprecated see {@link android.app.NotificationManager#EXTRA_AUTOMATIC_RULE_ID}.
*/
+ @Deprecated
public static final String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
/**
@@ -171,7 +192,11 @@
* service that has an {@link android.app.AutomaticZenRule#getConditionId()} equal to this
* {@link Condition#id}.
* @param condition the condition that has changed.
+ *
+ * @deprecated see
+ * {@link android.app.NotificationManager#setAutomaticZenRuleState(String, Condition)}.
*/
+ @Deprecated
public final void notifyCondition(Condition condition) {
if (condition == null) return;
notifyConditions(new Condition[]{ condition });
@@ -181,7 +206,11 @@
* Informs the notification manager that the state of one or more Conditions has changed. See
* {@link #notifyCondition(Condition)} for restrictions.
* @param conditions the changed conditions.
+ *
+ * @deprecated see
+ * {@link android.app.NotificationManager#setAutomaticZenRuleState(String, Condition)}.
*/
+ @Deprecated
public final void notifyConditions(Condition... conditions) {
if (!isBound() || conditions == null) return;
try {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 0e2ae83..6792c69 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -142,6 +142,7 @@
private static final String RULE_ATT_SNOOZING = "snoozing";
private static final String RULE_ATT_NAME = "name";
private static final String RULE_ATT_COMPONENT = "component";
+ private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity";
private static final String RULE_ATT_ZEN = "zen";
private static final String RULE_ATT_CONDITION_ID = "conditionId";
private static final String RULE_ATT_CREATION_TIME = "creationTime";
@@ -269,7 +270,7 @@
return buffer.toString();
}
- private Diff diff(ZenModeConfig to) {
+ public Diff diff(ZenModeConfig to) {
final Diff d = new Diff();
if (to == null) {
return d.addLine("config", "delete");
@@ -623,7 +624,6 @@
public static ZenRule readRuleXml(XmlPullParser parser) {
final ZenRule rt = new ZenRule();
rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
- rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
rt.zenMode = tryParseZenMode(zen, -1);
@@ -633,6 +633,12 @@
}
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
+ rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
+ rt.pkg = (rt.component != null)
+ ? rt.component.getPackageName()
+ : (rt.configurationActivity != null)
+ ? rt.configurationActivity.getPackageName()
+ : null;
rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
rt.condition = readConditionXml(parser);
@@ -649,7 +655,6 @@
public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
- out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
if (rule.name != null) {
out.attribute(null, RULE_ATT_NAME, rule.name);
}
@@ -657,6 +662,10 @@
if (rule.component != null) {
out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
}
+ if (rule.configurationActivity != null) {
+ out.attribute(null, RULE_ATT_CONFIG_ACTIVITY,
+ rule.configurationActivity.flattenToString());
+ }
if (rule.conditionId != null) {
out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
}
@@ -1452,12 +1461,15 @@
public Uri conditionId; // required for automatic
public Condition condition; // optional
public ComponentName component; // optional
+ public ComponentName configurationActivity; // optional
public String id; // required for automatic (unique)
@UnsupportedAppUsage
public long creationTime; // required for automatic
- public String enabler; // package name, only used for manual rules.
+ // package name, only used for manual rules when they have turned DND on.
+ public String enabler;
public ZenPolicy zenPolicy;
public boolean modified; // rule has been modified from initial creation
+ public String pkg;
public ZenRule() { }
@@ -1471,6 +1483,7 @@
conditionId = source.readParcelable(null);
condition = source.readParcelable(null);
component = source.readParcelable(null);
+ configurationActivity = source.readParcelable(null);
if (source.readInt() == 1) {
id = source.readString();
}
@@ -1480,6 +1493,7 @@
}
zenPolicy = source.readParcelable(null);
modified = source.readInt() == 1;
+ pkg = source.readString();
}
@Override
@@ -1501,6 +1515,7 @@
dest.writeParcelable(conditionId, 0);
dest.writeParcelable(condition, 0);
dest.writeParcelable(component, 0);
+ dest.writeParcelable(configurationActivity, 0);
if (id != null) {
dest.writeInt(1);
dest.writeString(id);
@@ -1516,6 +1531,7 @@
}
dest.writeParcelable(zenPolicy, 0);
dest.writeInt(modified ? 1 : 0);
+ dest.writeString(pkg);
}
@Override
@@ -1528,7 +1544,9 @@
.append(",zenMode=").append(Global.zenModeToString(zenMode))
.append(",conditionId=").append(conditionId)
.append(",condition=").append(condition)
+ .append(",pkg=").append(pkg)
.append(",component=").append(component)
+ .append(",configActivity=").append(configurationActivity)
.append(",creationTime=").append(creationTime)
.append(",enabler=").append(enabler)
.append(",zenPolicy=").append(zenPolicy)
@@ -1537,6 +1555,7 @@
}
/** @hide */
+ // TODO: add configuration activity
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
@@ -1600,6 +1619,9 @@
if (!Objects.equals(component, to.component)) {
d.addLine(item, "component", component, to.component);
}
+ if (!Objects.equals(configurationActivity, to.configurationActivity)) {
+ d.addLine(item, "configActivity", configurationActivity, to.configurationActivity);
+ }
if (!Objects.equals(id, to.id)) {
d.addLine(item, "id", id, to.id);
}
@@ -1615,6 +1637,9 @@
if (modified != to.modified) {
d.addLine(item, "modified", modified, to.modified);
}
+ if (pkg != to.pkg) {
+ d.addLine(item, "pkg", pkg, to.pkg);
+ }
}
@Override
@@ -1629,20 +1654,22 @@
&& Objects.equals(other.conditionId, conditionId)
&& Objects.equals(other.condition, condition)
&& Objects.equals(other.component, component)
+ && Objects.equals(other.configurationActivity, configurationActivity)
&& Objects.equals(other.id, id)
&& Objects.equals(other.enabler, enabler)
&& Objects.equals(other.zenPolicy, zenPolicy)
+ && Objects.equals(other.pkg, pkg)
&& other.modified == modified;
}
@Override
public int hashCode() {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, id, enabler, zenPolicy, modified);
+ component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
}
public boolean isAutomaticActive() {
- return enabled && !snoozing && component != null && isTrueOrUnknown();
+ return enabled && !snoozing && pkg != null && isTrueOrUnknown();
}
public boolean isTrueOrUnknown() {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a095b0d..f295b70 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -25,6 +25,7 @@
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
+import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -52,12 +53,12 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -211,7 +212,8 @@
private final Supplier<Long> mClockFunction;
private final Handler mHandler;
- Display mDisplay;
+ private Display mDisplay;
+ private Context mDisplayContext;
private int mDisplayState;
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
@@ -1038,6 +1040,7 @@
mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
mCaller.getHandler());
mDisplay = mIWallpaperEngine.mDisplay;
+ mDisplayContext = createDisplayContext(mDisplay);
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1050,6 +1053,18 @@
}
/**
+ * The {@link Context} with resources that match the current display the wallpaper is on.
+ * For multiple display environment, multiple engines can be created to render on each
+ * display, but these displays may have different densities. Use this context to get the
+ * corresponding resources for currently display, avoiding the context of the service.
+ *
+ * @return A {@link Context} for current display.
+ */
+ public Context getDisplayContext() {
+ return mDisplayContext;
+ }
+
+ /**
* Executes life cycle event and updates internal ambient mode state based on
* message sent from handler.
*
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index b1fc17a..8d4db54 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -96,7 +96,7 @@
* the hierarchy specified by the layoutId resource file.
*
* <p>This method is hidden because layoutId-based scenes should be
- * created by the caching factory method {@link Scene#getCurrentScene(View)}.</p>
+ * created by the caching factory method {@link Scene#getCurrentScene(ViewGroup)}.</p>
*
* @param sceneRoot The root of the hierarchy in which scene changes
* and transitions will take place.
@@ -194,28 +194,28 @@
}
/**
- * Set the scene that the given view is in. The current scene is set only
- * on the root view of a scene, not for every view in that hierarchy. This
+ * Set the scene that the given ViewGroup is in. The current scene is set only
+ * on the root ViewGroup of a scene, not for every view in that hierarchy. This
* information is used by Scene to determine whether there is a previous
* scene which should be exited before the new scene is entered.
*
- * @param sceneRoot The view on which the current scene is being set
+ * @param sceneRoot The ViewGroup on which the current scene is being set
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) {
+ static void setCurrentScene(@NonNull ViewGroup sceneRoot, @Nullable Scene scene) {
sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene);
}
/**
- * Gets the current {@link Scene} set on the given view. A scene is set on a view
- * only if that view is the scene root.
+ * Gets the current {@link Scene} set on the given ViewGroup. A scene is set on a ViewGroup
+ * only if that ViewGroup is the scene root.
*
- * @param sceneRoot The view on which the current scene will be returned
- * @return The current Scene set on this view. A value of null indicates that
+ * @param sceneRoot The ViewGroup on which the current scene will be returned
+ * @return The current Scene set on this ViewGroup. A value of null indicates that
* no Scene is currently set.
*/
@Nullable
- public static Scene getCurrentScene(@NonNull View sceneRoot) {
+ public static Scene getCurrentScene(@NonNull ViewGroup sceneRoot) {
return (Scene) sceneRoot.getTag(com.android.internal.R.id.current_scene);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ade7577..8b97e0e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -46,6 +46,7 @@
DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
+ DEFAULT_FLAGS.put("settings_slice_injection", "false");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9227249..699b34a 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1471,8 +1471,8 @@
// Note: don't need to use locked suffix because mContext is final.
private AutofillClient getClient() {
final AutofillClient client = mContext.getAutofillClient();
- if (client == null && sDebug) {
- Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
+ if (client == null && sVerbose) {
+ Log.v(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
+ mContext);
}
return client;
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index 97247aa..a65214a 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -32,6 +32,12 @@
}
@Override
+ public void onNat64PrefixEvent(int netId, boolean added, String prefixString,
+ int prefixLength) {
+ // default no-op
+ }
+
+ @Override
public void onPrivateDnsValidationEvent(int netId, String ipAddress,
String hostname, boolean validated) {
// default no-op
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 752624b..0d75de9 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -338,6 +338,11 @@
return renderNode->stagingProperties().hasOverlappingRendering();
}
+static jboolean android_view_RenderNode_getClipToBounds(jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ return renderNode->stagingProperties().getClipToBounds();
+}
+
static jboolean android_view_RenderNode_getClipToOutline(jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return renderNode->stagingProperties().getOutline().getShouldClip();
@@ -409,6 +414,11 @@
return !renderNode->stagingProperties().hasTransformMatrix();
}
+static jint android_view_RenderNode_getLayerType(jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ return static_cast<int>(renderNode->stagingProperties().layerProperties().type());
+}
+
// ----------------------------------------------------------------------------
// RenderProperties - computed getters
// ----------------------------------------------------------------------------
@@ -623,10 +633,12 @@
// ----------------------------------------------------------------------------
{ "nIsValid", "(J)Z", (void*) android_view_RenderNode_isValid },
{ "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType },
+ { "nGetLayerType", "(J)I", (void*) android_view_RenderNode_getLayerType },
{ "nSetLayerPaint", "(JJ)Z", (void*) android_view_RenderNode_setLayerPaint },
{ "nSetStaticMatrix", "(JJ)Z", (void*) android_view_RenderNode_setStaticMatrix },
{ "nSetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_setAnimationMatrix },
{ "nSetClipToBounds", "(JZ)Z", (void*) android_view_RenderNode_setClipToBounds },
+ { "nGetClipToBounds", "(J)Z", (void*) android_view_RenderNode_getClipToBounds },
{ "nSetClipBounds", "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
{ "nSetClipBoundsEmpty", "(J)Z", (void*) android_view_RenderNode_setClipBoundsEmpty },
{ "nSetProjectBackwards", "(JZ)Z", (void*) android_view_RenderNode_setProjectBackwards },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7032081..ff4591f 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -418,7 +418,7 @@
}
endmntent(fp);
- for (auto path : toUnmount) {
+ for (const auto& path : toUnmount) {
if (umount2(path.c_str(), MNT_DETACH)) {
ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno));
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7fa3e66..cc8927f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1642,6 +1642,12 @@
<permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
android:protectionLevel="signature" />
+ <!-- #SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can be increased
+ when the device is stationary in order to save power.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
+ android:protectionLevel="signature|privileged" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml
index ea7c009..f4aeff7 100644
--- a/core/res/res/values/colors_car.xml
+++ b/core/res/res/values/colors_car.xml
@@ -50,7 +50,7 @@
<color name="car_headline4_dark">@android:color/black</color>
<color name="car_headline4">@color/car_headline4_light</color>
- <color name="car_body1_light">@color/car_grey_100</color>
+ <color name="car_body1_light">@color/car_grey_50</color>
<color name="car_body1_dark">@color/car_grey_900</color>
<color name="car_body1">@color/car_body1_light</color>
@@ -58,7 +58,7 @@
<color name="car_body2_dark">@color/car_grey_700</color>
<color name="car_body2">@color/car_body2_light</color>
- <color name="car_body3_light">@android:color/white</color>
+ <color name="car_body3_light">@color/car_grey_400</color>
<color name="car_body3_dark">@android:color/black</color>
<color name="car_body3">@color/car_body3_light</color>
@@ -137,7 +137,7 @@
<color name="car_toast_background">#E6282a2d</color>
<!-- Misc colors -->
- <color name="car_highlight_light">@color/car_teal_700</color>
+ <color name="car_highlight_light">@color/car_teal_200</color>
<color name="car_highlight_dark">@color/car_teal_200</color>
<color name="car_highlight">@color/car_highlight_dark</color>
<color name="car_accent_light">@color/car_highlight_light</color>
@@ -148,6 +148,7 @@
<color name="car_user_switcher_user_image_fgcolor">@color/car_grey_900</color>
<!-- Color palette for cars -->
+ <color name="car_grey_972">#ff090A0C</color>
<color name="car_grey_958">#ff0e1013</color>
<color name="car_grey_928">#ff17181b</color>
<color name="car_grey_900">#ff202124</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 38caa36..dd0b1ee 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1165,6 +1165,10 @@
<!-- The default suggested battery % at which we enable battery saver automatically. -->
<integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
+ <!-- The app which will handle routine based automatic battery saver, if empty the UI for
+ routine based battery saver will be hidden -->
+ <string name="config_batterySaverScheduleProvider"></string>
+
<!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
plus this -->
<integer name="config_lowBatteryCloseWarningBump">5</integer>
@@ -3640,4 +3644,6 @@
(android.view.InputEventCompatProcessor). -->
<string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string>
+ <!-- Component name for the default module metadata provider on this device -->
+ <string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6f75d90..799d9d8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2978,6 +2978,11 @@
<public name="config_mediaMetadataBitmapMaxSize" />
</public-group>
+ <public-group type="color" first-id="0x0106001c">
+ <!-- @hide @SystemApi -->
+ <public name="system_notification_accent_color" />
+ </public-group>
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f959b44..87fdc1f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3463,6 +3463,7 @@
<java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
<java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
<java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
+ <java-symbol type="string" name="config_batterySaverScheduleProvider" />
<!-- For car devices -->
<java-symbol type="string" name="car_loading_profile" />
@@ -3519,4 +3520,6 @@
<java-symbol type="dimen" name="rounded_corner_radius" />
<java-symbol type="dimen" name="rounded_corner_radius_top" />
<java-symbol type="dimen" name="rounded_corner_radius_bottom" />
+
+ <java-symbol type="string" name="config_defaultModuleMetadataProvider" />
</resources>
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 55e21a7..514ea0c 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -517,6 +517,28 @@
}
@Test
+ public void testMalformedTransate_int() throws Exception {
+ try {
+ // The non-standard Linux access mode 3 should throw
+ // an IllegalArgumentException.
+ translateModePosixToPfd(O_RDWR | O_WRONLY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testMalformedTransate_string() throws Exception {
+ try {
+ // The non-standard Linux access mode 3 should throw
+ // an IllegalArgumentException.
+ translateModePosixToString(O_RDWR | O_WRONLY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
public void testTranslateMode_Invalid() throws Exception {
try {
translateModeStringToPosix("rwx");
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 96ab977..5a86885 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -278,13 +278,12 @@
}
}
- final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
- "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
- "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " +
- "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
- "esse cillum dolore eu fugiat nulla pariatur. " +
- "Excepteur sint occaecat cupidatat non proident, " +
- "sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+ + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
+ + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+ + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+ + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+ + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
final String so = "Lorem ipsum: single overlay.";
final String mo = "Lorem ipsum: multiple overlays.";
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 6d5276f..27986cc 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
@@ -1,17 +1,17 @@
/*
* 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.server.om.hosttest;
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index a174d77..86a8679 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -1,17 +1,17 @@
/*
* 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.server.om.hosttest.update_overlay_test;
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 61ef426..ff8c4f1 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -54,6 +54,7 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_RELATIVE_PATH := permissions
LOCAL_MODULE_STEM := com.android.settings.xml
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := com.android.settings.xml
include $(BUILD_PREBUILT)
@@ -63,6 +64,7 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_RELATIVE_PATH := permissions
LOCAL_MODULE_STEM := com.android.systemui.xml
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := com.android.systemui.xml
include $(BUILD_PREBUILT)
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index d6f08b9..3b1d44b 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -446,7 +446,21 @@
}
/**
- * Sets the clip bounds of the RenderNode.
+ * Gets whether or not a compositing layer is forced to be used. The default & recommended
+ * is false, as it is typically faster to avoid using compositing layers.
+ * See {@link #setUseCompositingLayer(boolean, Paint)}.
+ *
+ * @return true if a compositing layer is forced, false otherwise
+ */
+ public boolean getUseCompositingLayer() {
+ return nGetLayerType(mNativeRenderNode) != 0;
+ }
+
+ /**
+ * Sets the clip bounds of the RenderNode. If null, the clip bounds is removed from the
+ * RenderNode. If non-null, the RenderNode will be clipped to this rect. If
+ * {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the
+ * intersection of this rectangle and the bounds of the render node.
*
* @param rect the bounds to clip to. If null, the clip bounds are reset
* @return True if the clip bounds changed, false otherwise
@@ -460,16 +474,30 @@
}
/**
- * Set whether the Render node should clip itself to its bounds. This property is controlled by
- * the view's parent.
+ * Set whether the Render node should clip itself to its bounds. This defaults to true,
+ * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as
+ * better partial invalidation support. Clipping can be further restricted or controlled
+ * through the combination of this property as well as {@link #setClipBounds(Rect)}, which
+ * allows for a different clipping rectangle to be used in addition to or instead of the
+ * {@link #setLeftTopRightBottom(int, int, int, int)} or the RenderNode.
*
- * @param clipToBounds true if the display list should clip to its bounds
+ * @param clipToBounds true if the display list should clip to its bounds, false otherwise.
*/
public boolean setClipToBounds(boolean clipToBounds) {
return nSetClipToBounds(mNativeRenderNode, clipToBounds);
}
/**
+ * Returns whether or not the RenderNode is clipping to its bounds. See
+ * {@link #setClipToBounds(boolean)} and {@link #setLeftTopRightBottom(int, int, int, int)}
+ *
+ * @return true if the render node clips to its bounds, false otherwise.
+ */
+ public boolean getClipToBounds() {
+ return nGetClipToBounds(mNativeRenderNode);
+ }
+
+ /**
* Sets whether the RenderNode should be drawn immediately after the
* closest ancestor RenderNode containing a projection receiver.
*
@@ -1339,12 +1367,18 @@
private static native boolean nSetLayerType(long renderNode, int layerType);
@CriticalNative
+ private static native int nGetLayerType(long renderNode);
+
+ @CriticalNative
private static native boolean nSetLayerPaint(long renderNode, long paint);
@CriticalNative
private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
@CriticalNative
+ private static native boolean nGetClipToBounds(long renderNode);
+
+ @CriticalNative
private static native boolean nSetClipBounds(long renderNode, int left, int top,
int right, int bottom);
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
new file mode 100644
index 0000000..a58b47f
--- /dev/null
+++ b/libs/androidfw/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "libandroidfw_tests",
+ "host": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 441356b..22d587a 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -365,6 +365,6 @@
// structs with size fields (like Res_value, ResTable_entry) should be
// backwards and forwards compatible (aka checking the size field against
// sizeof(Res_value) might not be backwards compatible.
-TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+// TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
} // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0503f36..6585bfc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -208,6 +208,7 @@
"FrameInfoVisualizer.cpp",
"GpuMemoryTracker.cpp",
"HardwareBitmapUploader.cpp",
+ "HWUIProperties.sysprop",
"Interpolator.cpp",
"JankTracker.cpp",
"Layer.cpp",
diff --git a/libs/hwui/HWUIProperties.sysprop b/libs/hwui/HWUIProperties.sysprop
new file mode 100644
index 0000000..42191ca
--- /dev/null
+++ b/libs/hwui/HWUIProperties.sysprop
@@ -0,0 +1,9 @@
+owner: Platform
+module: "android.uirenderer"
+prop {
+ api_name: "use_vulkan"
+ type: Boolean
+ prop_name: "ro.hwui.use_vulkan"
+ scope: Public
+ access: Readonly
+}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 8067313..046ffc4 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -18,6 +18,7 @@
#include "Debug.h"
#include "DeviceInfo.h"
#include "SkTraceEventCommon.h"
+#include "HWUIProperties.sysprop.h"
#include <algorithm>
#include <cstdlib>
@@ -174,8 +175,13 @@
if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
return sRenderPipelineType;
}
+ bool useVulkan = use_vulkan().value_or(false);
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_RENDERER, prop, "skiagl");
+ if (useVulkan) {
+ property_get(PROPERTY_RENDERER, prop, "skiavk");
+ } else {
+ property_get(PROPERTY_RENDERER, prop, "skiagl");
+ }
if (!strcmp(prop, "skiavk")) {
ALOGD("Skia Vulkan Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaVulkan;
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 35cf707..de8777b 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -65,6 +65,7 @@
void applyColorTransform(ColorTransform transform);
bool hasText() const { return mHasText; }
+ size_t usedSize() const { return fUsed; }
private:
friend class RecordingCanvas;
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 04379ae..ddb7e4e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -98,15 +98,15 @@
LayerProperties& operator=(const LayerProperties& other);
+ // Strongly recommend using effectiveLayerType instead
+ LayerType type() const { return mType; }
+
private:
LayerProperties();
~LayerProperties();
void reset();
bool setColorFilter(SkColorFilter* filter);
- // Private since external users should go through properties().effectiveLayerType()
- LayerType type() const { return mType; }
-
friend class RenderProperties;
LayerType mType = LayerType::None;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index d7879e7..45f3a4c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -49,7 +49,7 @@
*/
class SkiaDisplayList {
public:
- size_t getUsedSize() { return allocator.usedSize(); }
+ size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
~SkiaDisplayList() {
/* Given that we are using a LinearStdAllocator to store some of the
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 15aec9f..4a2f57e 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -70,7 +70,8 @@
int slot = st.mCurrentTexture;
if (slot != BufferItem::INVALID_BUFFER_SLOT) {
*queueEmpty = true;
- mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
+ st.mCurrentDataSpace);
return mImageSlots[slot].mImage;
}
}
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 0d87776..d189a93 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -76,7 +76,7 @@
paint.setStrokeWidth(strokeWidth);
// fill column with each op
int middleCount = canvas.save(SaveFlags::MatrixClip);
- for (auto op : ops) {
+ for (const auto& op : ops) {
int innerCount = canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect);
canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index ee6beba..b11eaa9 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -251,7 +251,7 @@
}
TEST(PathParser, parseStringForData) {
- for (TestData testData : sTestDataSet) {
+ for (const TestData& testData : sTestDataSet) {
PathParser::ParseResult result;
// Test generated path data against the given data.
PathData pathData;
@@ -271,7 +271,7 @@
}
TEST(VectorDrawableUtils, createSkPathFromPathData) {
- for (TestData testData : sTestDataSet) {
+ for (const TestData& testData : sTestDataSet) {
SkPath expectedPath;
testData.skPathLamda(&expectedPath);
SkPath actualPath;
@@ -281,7 +281,7 @@
}
TEST(PathParser, parseAsciiStringForSkPath) {
- for (TestData testData : sTestDataSet) {
+ for (const TestData& testData : sTestDataSet) {
PathParser::ParseResult result;
size_t length = strlen(testData.pathString);
// Check the return value as well as the SkPath generated.
@@ -304,8 +304,8 @@
}
TEST(VectorDrawableUtils, morphPathData) {
- for (TestData fromData : sTestDataSet) {
- for (TestData toData : sTestDataSet) {
+ for (const TestData& fromData : sTestDataSet) {
+ for (const TestData& toData : sTestDataSet) {
bool canMorph = VectorDrawableUtils::canMorph(fromData.pathData, toData.pathData);
if (fromData.pathData == toData.pathData) {
EXPECT_TRUE(canMorph);
@@ -319,8 +319,8 @@
TEST(VectorDrawableUtils, interpolatePathData) {
// Interpolate path data with itself and every other path data
- for (TestData fromData : sTestDataSet) {
- for (TestData toData : sTestDataSet) {
+ for (const TestData& fromData : sTestDataSet) {
+ for (const TestData& toData : sTestDataSet) {
PathData outData;
bool success = VectorDrawableUtils::interpolatePathData(&outData, fromData.pathData,
toData.pathData, 0.5);
@@ -331,7 +331,7 @@
float fractions[] = {0, 0.00001, 0.28, 0.5, 0.7777, 0.9999999, 1};
// Now try to interpolate with a slightly modified version of self and expect success
- for (TestData fromData : sTestDataSet) {
+ for (const TestData& fromData : sTestDataSet) {
PathData toPathData = fromData.pathData;
for (size_t i = 0; i < toPathData.points.size(); i++) {
toPathData.points[i]++;
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 80d8e72..0a90f85 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,6 +89,10 @@
mLocked.animationPending = false;
+ mLocked.displayWidth = -1;
+ mLocked.displayHeight = -1;
+ mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
+
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
@@ -106,6 +110,15 @@
mLocked.lastFrameUpdatedTime = 0;
mLocked.buttonState = 0;
+
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+
+ loadResources();
+
+ if (mLocked.pointerIcon.isValid()) {
+ mLocked.pointerIconChanged = true;
+ updatePointerLocked();
+ }
}
PointerController::~PointerController() {
@@ -131,15 +144,23 @@
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
-
- if (!mLocked.viewport.isValid()) {
+ if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
return false;
}
- *outMinX = mLocked.viewport.logicalLeft;
- *outMinY = mLocked.viewport.logicalTop;
- *outMaxX = mLocked.viewport.logicalRight - 1;
- *outMaxY = mLocked.viewport.logicalBottom - 1;
+ *outMinX = 0;
+ *outMinY = 0;
+ switch (mLocked.displayOrientation) {
+ case DISPLAY_ORIENTATION_90:
+ case DISPLAY_ORIENTATION_270:
+ *outMaxX = mLocked.displayHeight - 1;
+ *outMaxY = mLocked.displayWidth - 1;
+ break;
+ default:
+ *outMaxX = mLocked.displayWidth - 1;
+ *outMaxY = mLocked.displayHeight - 1;
+ break;
+ }
return true;
}
@@ -210,12 +231,6 @@
*outY = mLocked.pointerY;
}
-int32_t PointerController::getDisplayId() const {
- AutoMutex _l(mLock);
-
- return mLocked.viewport.displayId;
-}
-
void PointerController::fade(Transition transition) {
AutoMutex _l(mLock);
@@ -340,57 +355,48 @@
void PointerController::reloadPointerResources() {
AutoMutex _l(mLock);
- loadResourcesLocked();
+ loadResources();
+
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ mLocked.additionalMouseResources.clear();
+ mLocked.animationResources.clear();
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources);
+ }
+
+ mLocked.presentationChanged = true;
updatePointerLocked();
}
-/**
- * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
- * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
- */
-static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
- if (viewport.orientation == DISPLAY_ORIENTATION_90
- || viewport.orientation == DISPLAY_ORIENTATION_270) {
- width = viewport.deviceHeight;
- height = viewport.deviceWidth;
- } else {
- width = viewport.deviceWidth;
- height = viewport.deviceHeight;
- }
-}
-
-void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
AutoMutex _l(mLock);
- if (viewport == mLocked.viewport) {
- return;
+
+ // Adjust to use the display's unrotated coordinate frame.
+ if (orientation == DISPLAY_ORIENTATION_90
+ || orientation == DISPLAY_ORIENTATION_270) {
+ int32_t temp = height;
+ height = width;
+ width = temp;
}
- const DisplayViewport oldViewport = mLocked.viewport;
- mLocked.viewport = viewport;
-
- int32_t oldDisplayWidth, oldDisplayHeight;
- getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
- int32_t newDisplayWidth, newDisplayHeight;
- getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
-
- // Reset cursor position to center if size or display changed.
- if (oldViewport.displayId != viewport.displayId
- || oldDisplayWidth != newDisplayWidth
- || oldDisplayHeight != newDisplayHeight) {
+ if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
+ mLocked.displayWidth = width;
+ mLocked.displayHeight = height;
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
mLocked.pointerX = (minX + maxX) * 0.5f;
mLocked.pointerY = (minY + maxY) * 0.5f;
- // Reload icon resources for density may be changed.
- loadResourcesLocked();
} else {
mLocked.pointerX = 0;
mLocked.pointerY = 0;
}
fadeOutAndReleaseAllSpotsLocked();
- } else if (oldViewport.orientation != viewport.orientation) {
+ }
+
+ if (mLocked.displayOrientation != orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
@@ -399,37 +405,37 @@
float temp;
// Undo the previous rotation.
- switch (oldViewport.orientation) {
+ switch (mLocked.displayOrientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
- x = oldViewport.deviceHeight - y;
+ x = mLocked.displayWidth - y;
y = temp;
break;
case DISPLAY_ORIENTATION_180:
- x = oldViewport.deviceWidth - x;
- y = oldViewport.deviceHeight - y;
+ x = mLocked.displayWidth - x;
+ y = mLocked.displayHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = y;
- y = oldViewport.deviceWidth - temp;
+ y = mLocked.displayHeight - temp;
break;
}
// Perform the new rotation.
- switch (viewport.orientation) {
+ switch (orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
- y = viewport.deviceHeight - temp;
+ y = mLocked.displayWidth - temp;
break;
case DISPLAY_ORIENTATION_180:
- x = viewport.deviceWidth - x;
- y = viewport.deviceHeight - y;
+ x = mLocked.displayWidth - x;
+ y = mLocked.displayHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
- x = viewport.deviceWidth - y;
+ x = mLocked.displayHeight - y;
y = temp;
break;
}
@@ -438,6 +444,7 @@
// and save the results.
mLocked.pointerX = x - 0.5f;
mLocked.pointerY = y - 0.5f;
+ mLocked.displayOrientation = orientation;
}
updatePointerLocked();
@@ -607,16 +614,11 @@
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
}
-void PointerController::updatePointerLocked() REQUIRES(mLock) {
- if (!mLocked.viewport.isValid()) {
- return;
- }
-
+void PointerController::updatePointerLocked() {
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
- mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -727,18 +729,8 @@
}
}
-void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+void PointerController::loadResources() {
mPolicy->loadPointerResources(&mResources);
-
- if (mLocked.presentation == PRESENTATION_POINTER) {
- mLocked.additionalMouseResources.clear();
- mLocked.animationResources.clear();
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
- &mLocked.animationResources);
- }
-
- mLocked.pointerIconChanged = true;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index a32cc42..7f4e5a5 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,7 +23,6 @@
#include <vector>
#include <ui/DisplayInfo.h>
-#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
@@ -97,7 +96,6 @@
virtual int32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
- virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
@@ -108,7 +106,7 @@
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(const DisplayViewport& viewport);
+ void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
@@ -158,7 +156,9 @@
size_t animationFrameIndex;
nsecs_t lastFrameUpdatedTime;
- DisplayViewport viewport;
+ int32_t displayWidth;
+ int32_t displayHeight;
+ int32_t displayOrientation;
InactivityTimeout inactivityTimeout;
@@ -182,7 +182,7 @@
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
- } mLocked GUARDED_BY(mLock);
+ } mLocked;
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
- void loadResourcesLocked();
+ void loadResources();
};
} // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..eb2bc98 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,16 +144,13 @@
}
}
- // Resize and/or reparent sprites if needed.
+ // Resize sprites if needed.
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
- if (update.state.surfaceControl == nullptr) {
- continue;
- }
- if (update.state.wantSurfaceVisible()) {
+ if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
@@ -173,12 +170,6 @@
}
}
}
-
- // If surface is a new one, we have to set right layer stack.
- if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
- t.setLayerStack(update.state.surfaceControl, update.state.displayId);
- needApplyTransaction = true;
- }
}
if (needApplyTransaction) {
t.apply();
@@ -245,7 +236,7 @@
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -454,15 +445,6 @@
}
}
-void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
- AutoMutex _l(mController->mLock);
-
- if (mLocked.state.displayId != displayId) {
- mLocked.state.displayId = displayId;
- invalidateLocked(DIRTY_DISPLAY_ID);
- }
-}
-
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..31e43e9 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,9 +125,6 @@
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
-
- /* Sets the id of the display where the sprite should be shown. */
- virtual void setDisplayId(int32_t displayId) = 0;
};
/*
@@ -173,7 +170,6 @@
DIRTY_LAYER = 1 << 4,
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
- DIRTY_DISPLAY_ID = 1 << 7,
};
/* Describes the state of a sprite.
@@ -184,7 +180,7 @@
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
- positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
+ positionX(0), positionY(0), layer(0), alpha(1.0f),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
@@ -197,7 +193,6 @@
int32_t layer;
float alpha;
SpriteTransformationMatrix transformationMatrix;
- int32_t displayId;
sp<SurfaceControl> surfaceControl;
int32_t surfaceWidth;
@@ -230,7 +225,6 @@
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
- virtual void setDisplayId(int32_t displayId);
inline const SpriteState& getStateLocked() const {
return mLocked.state;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 18d36eb..0057875 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.content.ContentProvider;
@@ -1680,37 +1679,6 @@
public native boolean isPlaying();
/**
- * Gets the current buffering management params used by the source component.
- * Calling it only after {@code setDataSource} has been called.
- * Each type of data source might have different set of default params.
- *
- * @return the current buffering management params used by the source component.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized, or {@code setDataSource} has not been called.
- * @hide
- */
- @NonNull
- @TestApi
- public native BufferingParams getBufferingParams();
-
- /**
- * Sets buffering management params.
- * The object sets its internal BufferingParams to the input, except that the input is
- * invalid or not supported.
- * Call it only after {@code setDataSource} has been called.
- * The input is a hint to MediaPlayer.
- *
- * @param params the buffering management params.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released, or {@code setDataSource} has not been called.
- * @throws IllegalArgumentException if params is invalid or not supported.
- * @hide
- */
- @TestApi
- public native void setBufferingParams(@NonNull BufferingParams params);
-
- /**
* Change playback speed of audio by resampling the audio.
* <p>
* Specifies resampling as audio mode for variable rate playback, i.e.,
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d4b1c7f..b137ce2 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -544,32 +544,55 @@
public native long getCurrentPosition();
/**
- * Gets the duration of the file.
+ * Gets the duration of the dsd.
*
+ * @param dsd the descriptor of data source of which you want to get duration
* @return the duration in milliseconds, if no duration is available
* (for example, if streaming live content), -1 is returned.
+ * @throws NullPointerException if dsd is null
*/
- public native long getDuration();
+ public long getDuration(@NonNull DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return -1;
+ }
+
+ return native_getDuration(sourceInfo.mId);
+ }
+
+ private native long native_getDuration(long srcId);
/**
- * Gets the current buffered media source position received through progressive downloading.
+ * Gets the buffered media source position of given dsd.
* For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
* has already been played indicates that the next 3000 milliseconds of the
* content to play has been buffered.
*
+ * @param dsd the descriptor of data source of which you want to get buffered position
* @return the current buffered media source position in milliseconds
+ * @throws NullPointerException if dsd is null
*/
- public long getBufferedPosition() {
- // Use cached buffered percent for now.
- int bufferedPercentage;
- synchronized (mSrcLock) {
- if (mCurrentSourceInfo == null) {
- bufferedPercentage = 0;
- } else {
- bufferedPercentage = mCurrentSourceInfo.mBufferedPercentage.get();
- }
+ public long getBufferedPosition(@NonNull DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
}
- return getDuration() * bufferedPercentage / 100;
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return 0;
+ }
+
+ // Use cached buffered percent for now.
+ int bufferedPercentage = sourceInfo.mBufferedPercentage.get();
+
+ long duration = getDuration(dsd);
+ if (duration < 0) {
+ duration = 0;
+ }
+
+ return duration * bufferedPercentage / 100;
}
/**
@@ -1467,7 +1490,6 @@
private native PersistableBundle native_getMetrics();
-
/**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
@@ -1505,7 +1527,6 @@
private native void native_setBufferingParams(@NonNull BufferingParams params);
-
/**
* Sets playback rate using {@link PlaybackParams}. The object sets its internal
* PlaybackParams to the input. This allows the object to resume at previous speed
@@ -1969,19 +1990,31 @@
/**
* Returns a List of track information.
*
+ * @param dsd the descriptor of data source of which you want to get track info
* @return List of track info. The total number of tracks is the array length.
* Must be called again if an external timed text source has been added after
* addTimedTextSource method is called.
* @throws IllegalStateException if it is called in an invalid state.
+ * @throws NullPointerException if dsd is null
*/
- public @NonNull List<TrackInfo> getTrackInfo() {
- TrackInfo[] trackInfo = getInbandTrackInfo();
+
+ public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return new ArrayList<TrackInfo>(0);
+ }
+
+ TrackInfo[] trackInfo = getInbandTrackInfo(sourceInfo);
return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0));
}
- private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
+ private TrackInfo[] getInbandTrackInfo(SourceInfo sourceInfo) throws IllegalStateException {
PlayerMessage request = PlayerMessage.newBuilder()
.addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO))
+ .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
.build();
PlayerMessage response = invoke(request);
if (response == null) {
@@ -2001,9 +2034,10 @@
/**
* Returns the index of the audio, video, or subtitle track currently selected for playback,
- * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
- * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
+ * The return value is an index into the array returned by {@link #getTrackInfo}, and can
+ * be used in calls to {@link #selectTrack} or {@link #deselectTrack}.
*
+ * @param dsd the descriptor of data source of which you want to get selected track
* @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
* {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
* {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
@@ -2011,14 +2045,24 @@
* a negative integer is returned when there is no selected track for {@code trackType} or
* when {@code trackType} is not one of audio, video, or subtitle.
* @throws IllegalStateException if called after {@link #close()}
+ * @throws NullPointerException if dsd is null
*
- * @see #getTrackInfo()
- * @see #selectTrack(int)
- * @see #deselectTrack(int)
+ * @see #getTrackInfo
+ * @see #selectTrack
+ * @see #deselectTrack
*/
- public int getSelectedTrack(int trackType) {
+ public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) {
+ if (dsd == null) {
+ throw new NullPointerException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return -1;
+ }
+
PlayerMessage request = PlayerMessage.newBuilder()
.addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK))
+ .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
.addValues(Value.newBuilder().setInt32Value(trackType))
.build();
PlayerMessage response = invoke(request);
@@ -2049,19 +2093,20 @@
* In addition, the support for selecting an audio track at runtime is pretty limited
* in that an audio track can only be selected in the <em>Prepared</em> state.
* </p>
+ * @param dsd the descriptor of data source of which you want to select track
* @param index the index of the track to be selected. The valid range of the index
* is 0..total number of track - 1. The total number of tracks as well as the type of
- * each individual track can be found by calling {@link #getTrackInfo()} method.
+ * each individual track can be found by calling {@link #getTrackInfo} method.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
// This is an asynchronous call.
- public Object selectTrack(int index) {
+ public Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
@Override
void process() {
- selectOrDeselectTrack(index, true /* select */);
+ selectOrDeselectTrack(dsd, index, true /* select */);
}
});
}
@@ -2073,28 +2118,37 @@
* deselected. If the timed text track identified by index has not been
* selected before, it throws an exception.
* </p>
+ * @param dsd the descriptor of data source of which you want to deselect track
* @param index the index of the track to be deselected. The valid range of the index
* is 0..total number of tracks - 1. The total number of tracks as well as the type of
- * each individual track can be found by calling {@link #getTrackInfo()} method.
+ * each individual track can be found by calling {@link #getTrackInfo} method.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
// This is an asynchronous call.
- public Object deselectTrack(int index) {
+ public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
@Override
void process() {
- selectOrDeselectTrack(index, false /* select */);
+ selectOrDeselectTrack(dsd, index, false /* select */);
}
});
}
- private void selectOrDeselectTrack(int index, boolean select)
- throws IllegalStateException {
+ private void selectOrDeselectTrack(@NonNull DataSourceDesc dsd, int index, boolean select) {
+ if (dsd == null) {
+ throw new IllegalArgumentException("non-null dsd is expected");
+ }
+ SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo == null) {
+ return;
+ }
+
PlayerMessage request = PlayerMessage.newBuilder()
.addValues(Value.newBuilder().setInt32Value(
select ? INVOKE_ID_SELECT_TRACK : INVOKE_ID_DESELECT_TRACK))
+ .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
.addValues(Value.newBuilder().setInt32Value(index))
.build();
invoke(request);
@@ -2568,7 +2622,7 @@
* Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
* {@link TimedMetaData}.
*
- * @see MediaPlayer2#selectTrack(int)
+ * @see MediaPlayer2#selectTrack
* @see MediaPlayer2.OnTimedMetaDataAvailableListener
* @see TimedMetaData
*
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
deleted file mode 100644
index a426552..0000000
--- a/media/java/android/media/MediaPlayerBase.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright 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.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Base class for all media players that want media session.
- */
-public abstract class MediaPlayerBase implements AutoCloseable {
- /**
- * @hide
- */
- @IntDef({
- PLAYER_STATE_IDLE,
- PLAYER_STATE_PAUSED,
- PLAYER_STATE_PLAYING,
- PLAYER_STATE_ERROR })
- @Retention(RetentionPolicy.SOURCE)
- public @interface PlayerState {}
-
- /**
- * @hide
- */
- @IntDef({
- BUFFERING_STATE_UNKNOWN,
- BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
- BUFFERING_STATE_BUFFERING_AND_STARVED,
- BUFFERING_STATE_BUFFERING_COMPLETE })
- @Retention(RetentionPolicy.SOURCE)
- public @interface BuffState {}
-
- /**
- * State when the player is idle, and needs configuration to start playback.
- */
- public static final int PLAYER_STATE_IDLE = 0;
-
- /**
- * State when the player's playback is paused
- */
- public static final int PLAYER_STATE_PAUSED = 1;
-
- /**
- * State when the player's playback is ongoing
- */
- public static final int PLAYER_STATE_PLAYING = 2;
-
- /**
- * State when the player is in error state and cannot be recovered self.
- */
- public static final int PLAYER_STATE_ERROR = 3;
-
- /**
- * Buffering state is unknown.
- */
- public static final int BUFFERING_STATE_UNKNOWN = 0;
-
- /**
- * Buffering state indicating the player is buffering but enough has been buffered
- * for this player to be able to play the content.
- * See {@link #getBufferedPosition()} for how far is buffered already.
- */
- public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
-
- /**
- * Buffering state indicating the player is buffering, but the player is currently starved
- * for data, and cannot play.
- */
- public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
-
- /**
- * Buffering state indicating the player is done buffering, and the remainder of the content is
- * available for playback.
- */
- public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3;
-
- /**
- * Starts or resumes playback.
- */
- public abstract void play();
-
- /**
- * Prepares the player for playback.
- * See {@link PlayerEventCallback#onMediaPrepared(MediaPlayerBase, DataSourceDesc)} for being
- * notified when the preparation phase completed. During this time, the player may allocate
- * resources required to play, such as audio and video decoders.
- */
- public abstract void prepare();
-
- /**
- * Pauses playback.
- */
- public abstract void pause();
-
- /**
- * Resets the MediaPlayerBase to its uninitialized state.
- */
- public abstract void reset();
-
- /**
- *
- */
- public abstract void skipToNext();
-
- /**
- * Moves the playback head to the specified position
- * @param pos the new playback position expressed in ms.
- */
- public abstract void seekTo(long pos);
-
- public static final long UNKNOWN_TIME = -1;
-
- /**
- * Gets the current playback head position.
- * @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown.
- */
- public long getCurrentPosition() { return UNKNOWN_TIME; }
-
- /**
- * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown.
- * @return the duration in ms, or {@link #UNKNOWN_TIME}.
- */
- public long getDuration() { return UNKNOWN_TIME; }
-
- /**
- * Gets the buffered position of current playback, or {@link #UNKNOWN_TIME} if unknown.
- * @return the buffered position in ms, or {@link #UNKNOWN_TIME}.
- */
- public long getBufferedPosition() { return UNKNOWN_TIME; }
-
- /**
- * Returns the current player state.
- * See also {@link PlayerEventCallback#onPlayerStateChanged(MediaPlayerBase, int)} for
- * notification of changes.
- * @return the current player state
- */
- public abstract @PlayerState int getPlayerState();
-
- /**
- * Returns the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- * @return the buffering state.
- */
- public abstract @BuffState int getBufferingState();
-
- /**
- * Sets the {@link AudioAttributes} to be used during the playback of the media.
- *
- * @param attributes non-null <code>AudioAttributes</code>.
- */
- public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
-
- /**
- * Returns AudioAttributes that media player has.
- */
- public abstract @Nullable AudioAttributes getAudioAttributes();
-
- /**
- * Sets the data source to be played.
- * @param dsd
- */
- public abstract void setDataSource(@NonNull DataSourceDesc dsd);
-
- /**
- * Sets the data source that will be played immediately after the current one is done playing.
- * @param dsd
- */
- public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
-
- /**
- * Sets the list of data sources that will be sequentially played after the current one. Each
- * data source is played immediately after the previous one is done playing.
- * @param dsds
- */
- public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
-
- /**
- * Returns the current data source.
- * @return the current data source, or null if none is set, or none available to play.
- */
- public abstract @Nullable DataSourceDesc getCurrentDataSource();
-
- /**
- * Configures the player to loop on the current data source.
- * @param loop true if the current data source is meant to loop.
- */
- public abstract void loopCurrent(boolean loop);
-
- /**
- * Sets the playback speed.
- * A value of 1.0f is the default playback value.
- * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
- * before using negative values.<br>
- * After changing the playback speed, it is recommended to query the actual speed supported
- * by the player, see {@link #getPlaybackSpeed()}.
- * @param speed
- */
- public abstract void setPlaybackSpeed(float speed);
-
- /**
- * Returns the actual playback speed to be used by the player when playing.
- * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
- * @return the actual playback speed
- */
- public float getPlaybackSpeed() { return 1.0f; }
-
- /**
- * Indicates whether reverse playback is supported.
- * Reverse playback is indicated by negative playback speeds, see
- * {@link #setPlaybackSpeed(float)}.
- * @return true if reverse playback is supported.
- */
- public boolean isReversePlaybackSupported() { return false; }
-
- /**
- * Sets the volume of the audio of the media to play, expressed as a linear multiplier
- * on the audio samples.
- * Note that this volume is specific to the player, and is separate from stream volume
- * used across the platform.<br>
- * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
- * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
- * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
- */
- public abstract void setPlayerVolume(float volume);
-
- /**
- * Returns the current volume of this player to this player.
- * Note that it does not take into account the associated stream volume.
- * @return the player volume.
- */
- public abstract float getPlayerVolume();
-
- /**
- * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
- */
- public float getMaxPlayerVolume() { return 1.0f; }
-
- /**
- * Adds a callback to be notified of events for this player.
- * @param e the {@link Executor} to be used for the events.
- * @param cb the callback to receive the events.
- */
- public abstract void registerPlayerEventCallback(@NonNull Executor e,
- @NonNull PlayerEventCallback cb);
-
- /**
- * Removes a previously registered callback for player events
- * @param cb the callback to remove
- */
- public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
-
- /**
- * A callback class to receive notifications for events on the media player.
- * See {@link MediaPlayerBase#registerPlayerEventCallback(Executor, PlayerEventCallback)} to
- * register this callback.
- */
- public static abstract class PlayerEventCallback {
- /**
- * Called when the player's current data source has changed.
- *
- * @param mpb the player whose data source changed.
- * @param dsd the new current data source. null, if no more data sources available.
- */
- public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
- @Nullable DataSourceDesc dsd) { }
- /**
- * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
- * referenced by the given data source.
- * @param mpb the player that is prepared.
- * @param dsd the data source that the player is prepared to play.
- */
- public void onMediaPrepared(@NonNull MediaPlayerBase mpb, @NonNull DataSourceDesc dsd) { }
-
- /**
- * Called to indicate that the state of the player has changed.
- * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
- * @param mpb the player whose state has changed.
- * @param state the new state of the player.
- */
- public void onPlayerStateChanged(@NonNull MediaPlayerBase mpb, @PlayerState int state) { }
-
- /**
- * Called to report buffering events for a data source.
- * @param mpb the player that is buffering
- * @param dsd the data source for which buffering is happening.
- * @param state the new buffering state.
- */
- public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb,
- @NonNull DataSourceDesc dsd, @BuffState int state) { }
-
- /**
- * Called to indicate that the playback speed has changed.
- * @param mpb the player that has changed the playback speed.
- * @param speed the new playback speed.
- */
- public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
-
- /**
- * Called to indicate that {@link #seekTo(long)} is completed.
- *
- * @param mpb the player that has completed seeking.
- * @param position the previous seeking request.
- * @see #seekTo(long)
- */
- public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
- }
-
-}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index e25e6a5..7481fff 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -97,9 +97,10 @@
shared_libs: [
"android.hardware.cas@1.0", // for CasManager. VNDK???
"android.hardware.cas.native@1.0", // CasManager. VNDK???
+ "android.hidl.allocator@1.0",
+ "libhidlmemory",
"libbinder",
"libgui", // for VideoFrameScheduler
- "libhidlallocatorutils",
"libhidlbase", // VNDK???
"libpowermanager", // for JWakeLock. to be removed
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 5dd01b0..76bbce7 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -39,7 +39,6 @@
#include "utils/Errors.h" // for status_t
#include "utils/KeyedVector.h"
#include "utils/String8.h"
-#include "android_media_BufferingParams.h"
#include "android_media_MediaDataSource.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_PlaybackParams.h"
@@ -94,7 +93,6 @@
};
static fields_t fields;
-static BufferingParams::fields_t gBufferingParamsFields;
static PlaybackParams::fields_t gPlaybackParamsFields;
static SyncParams::fields_t gSyncParamsFields;
static VolumeShaperHelper::fields_t gVolumeShaperFields;
@@ -370,50 +368,6 @@
setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
}
-static jobject
-android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- BufferingParams bp;
- BufferingSettings &settings = bp.settings;
- process_media_player_call(
- env, thiz, mp->getBufferingSettings(&settings),
- "java/lang/IllegalStateException", "unexpected error");
- if (env->ExceptionCheck()) {
- return nullptr;
- }
- ALOGV("getBufferingSettings:{%s}", settings.toString().string());
-
- return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static void
-android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
-{
- if (params == NULL) {
- return;
- }
-
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- BufferingParams bp;
- bp.fillFromJobject(env, gBufferingParamsFields, params);
- ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
-
- process_media_player_call(
- env, thiz, mp->setBufferingSettings(bp.settings),
- "java/lang/IllegalStateException", "unexpected error");
-}
-
static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
@@ -976,8 +930,6 @@
env->DeleteLocalRef(clazz);
- gBufferingParamsFields.init(env);
-
// Modular DRM
FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
if (clazz) {
@@ -1426,8 +1378,6 @@
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
- {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
- {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 8b6009e..7e6a8ab 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -820,7 +820,7 @@
}
static jlong
-android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz, jlong srcId)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -828,7 +828,7 @@
return 0;
}
int64_t msec;
- process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
+ process_media_player_call( env, thiz, mp->getDuration(srcId, &msec), NULL, NULL );
ALOGV("getDuration: %lld (msec)", (long long)msec);
return (jlong) msec;
}
@@ -1408,7 +1408,7 @@
{"native_seekTo", "(JI)V", (void *)android_media_MediaPlayer2_seekTo},
{"native_pause", "()V", (void *)android_media_MediaPlayer2_pause},
{"getCurrentPosition", "()J", (void *)android_media_MediaPlayer2_getCurrentPosition},
- {"getDuration", "()J", (void *)android_media_MediaPlayer2_getDuration},
+ {"native_getDuration", "(J)J", (void *)android_media_MediaPlayer2_getDuration},
{"native_release", "()V", (void *)android_media_MediaPlayer2_release},
{"native_reset", "()V", (void *)android_media_MediaPlayer2_reset},
{"native_setAudioAttributes", "(Landroid/media/AudioAttributes;)Z", (void *)android_media_MediaPlayer2_setAudioAttributes},
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index 1d67286..a2a628d 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -25,13 +25,15 @@
android:orientation="vertical"
android:gravity="center">
- <ImageView android:id="@+id/user_avatar"
+ <ImageView
+ android:id="@+id/user_avatar"
android:layout_width="@dimen/car_user_switcher_image_avatar_size"
android:layout_height="@dimen/car_user_switcher_image_avatar_size"
- android:background="@drawable/car_button_ripple_background_light"
+ android:background="?android:attr/selectableItemBackground"
android:gravity="center"/>
- <TextView android:id="@+id/user_name"
+ <TextView
+ android:id="@+id/user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/car_user_switcher_vertical_spacing_between_name_and_avatar"
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index 6cd70d6..e8c5134cd 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -19,12 +19,10 @@
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/car_user_switcher_background_color"
android:visibility="gone">
<LinearLayout
android:id="@+id/container"
- android:background="@color/car_user_switcher_background_color"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -38,7 +36,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/car_user_switcher_margin_top"
- android:theme="@style/Theme.Car.Light.List"
+ android:theme="@style/PagedListTheme"
app:verticallyCenterListContent="true"
app:showPagedListViewDivider="false"
app:gutter="both"
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 141b28a..72ec8d8 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -68,7 +68,7 @@
android:orientation="vertical">
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
+ android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index b67ce15..052566d 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -20,7 +20,7 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@android:color/black"
+ android:background="@drawable/system_bar_background"
android:orientation="vertical">
<LinearLayout
android:id="@id/nav_buttons"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 46e60db..4fa877f 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -20,7 +20,7 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@android:color/black"
+ android:background="@drawable/system_bar_background"
android:orientation="vertical">
<LinearLayout
diff --git a/packages/CarSystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
index 6f19cfc..bf96c00 100644
--- a/packages/CarSystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -35,7 +35,7 @@
android:layout_centerVertical="true"
android:layout_width="@dimen/car_qs_footer_icon_width"
android:layout_height="@dimen/car_qs_footer_icon_height"
- android:background="@drawable/ripple_drawable"
+ android:background="?android:attr/selectableItemBackground"
android:focusable="true">
<ImageView
diff --git a/packages/CarSystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
index dfa48c3..d923e0f 100644
--- a/packages/CarSystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -21,8 +21,7 @@
android:layout_height="wrap_content"
android:background="@color/car_qs_background_primary"
android:orientation="vertical"
- android:elevation="4dp"
- android:theme="@android:style/Theme">
+ android:elevation="4dp">
<include layout="@layout/car_status_bar_header"/>
<include layout="@layout/car_qs_footer"/>
@@ -39,7 +38,7 @@
android:id="@+id/user_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:theme="@style/Theme.Car.Light.List"
+ android:theme="@style/PagedListTheme"
app:showPagedListViewDivider="false"
app:gutter="both"
app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 141b28a..72ec8d8 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -68,7 +68,7 @@
android:orientation="vertical">
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
+ android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 7b3333e..1dca10a 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -21,7 +21,7 @@
android:id="@+id/car_top_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@android:color/black"
+ android:background="@drawable/system_bar_background"
android:orientation="vertical">
<RelativeLayout
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 572737f..c527711 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -51,7 +51,6 @@
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
- <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.notifications.NotificationsUI</item>
</string-array>
diff --git a/packages/CarSystemUI/res/values/themes.xml b/packages/CarSystemUI/res/values/themes.xml
new file mode 100644
index 0000000..8a5961e
--- /dev/null
+++ b/packages/CarSystemUI/res/values/themes.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+
+<resources>
+ <!--This Theme contains attributes required for components from the car support lib -->
+ <style name="PagedListTheme" parent="Theme.CarSupportWrapper.NoActionBar">
+ </style>
+</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 730c3e3..a442426 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -52,6 +52,9 @@
* is idling or moving, {@code false} otherwise.
*/
public boolean isCurrentlyDriving() {
+ if (mDrivingStateManager == null) {
+ return false;
+ }
try {
CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
if (currentState != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 9699294..7177821 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -19,9 +19,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
@@ -83,15 +81,13 @@
@SuppressLint("HardwareIds")
@Override
protected void updateConnectivity() {
- WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, OFF);
- final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+ final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+ String macAddress = null;
+ if (macAddresses != null && macAddresses.length > 0) {
+ macAddress = macAddresses[0];
+ }
- if (macRandomizationMode == ON && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
- mWifiMacAddress.setSummary(R.string.wifi_status_mac_randomized);
- } else if (TextUtils.isEmpty(macAddress)
- || WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+ if (TextUtils.isEmpty(macAddress)) {
mWifiMacAddress.setSummary(R.string.status_unavailable);
} else {
mWifiMacAddress.setSummary(macAddress);
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index f7b16f8..c8c05a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -22,6 +22,7 @@
import android.os.PowerManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
+import android.text.TextUtils;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -176,4 +177,22 @@
setAutoBatterySaverTriggerLevel(context, level);
}
}
+
+ /**
+ * Reverts battery saver schedule mode to none if we are in a bad state where routine mode
+ * is selected but no app is configured to actually provide the signal.
+ * @param context a valid context
+ */
+ public static void revertScheduleToNoneIfNeeded(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ final int currentMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ boolean providerConfigured = !TextUtils.isEmpty(context.getString(
+ com.android.internal.R.string.config_batterySaverScheduleProvider));
+ if (currentMode == PowerManager.POWER_SAVER_MODE_DYNAMIC && !providerConfigured) {
+ Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
new file mode 100644
index 0000000..9af0670
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -0,0 +1,189 @@
+/*
+ * 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.settingslib.location;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.format.DateUtils;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Retrieves the information of applications which accessed location recently.
+ */
+public class RecentLocationAccesses {
+ private static final String TAG = RecentLocationAccesses.class.getSimpleName();
+ @VisibleForTesting
+ static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+ // Keep last 24 hours of location app information.
+ private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
+
+ @VisibleForTesting
+ static final int[] LOCATION_OPS = new int[]{
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_COARSE_LOCATION,
+ };
+
+ private final PackageManager mPackageManager;
+ private final Context mContext;
+ private final IconDrawableFactory mDrawableFactory;
+
+ public RecentLocationAccesses(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mDrawableFactory = IconDrawableFactory.newInstance(context);
+ }
+
+ /**
+ * Fills a list of applications which queried location recently within specified time.
+ * Apps are sorted by recency. Apps with more recent location accesses are in the front.
+ */
+ public List<Access> getAppList() {
+ // Retrieve a location usage list from AppOps
+ AppOpsManager aoManager =
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+
+ final int appOpsCount = appOps != null ? appOps.size() : 0;
+
+ // Process the AppOps list and generate a preference list.
+ ArrayList<Access> accesses = new ArrayList<>(appOpsCount);
+ final long now = System.currentTimeMillis();
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final List<UserHandle> profiles = um.getUserProfiles();
+
+ for (int i = 0; i < appOpsCount; ++i) {
+ AppOpsManager.PackageOps ops = appOps.get(i);
+ // Don't show the Android System in the list - it's not actionable for the user.
+ // Also don't show apps belonging to background users except managed users.
+ String packageName = ops.getPackageName();
+ int uid = ops.getUid();
+ int userId = UserHandle.getUserId(uid);
+ boolean isAndroidOs =
+ (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
+ if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+ continue;
+ }
+ Access access = getAccessFromOps(now, ops);
+ if (access != null) {
+ accesses.add(access);
+ }
+ }
+ return accesses;
+ }
+
+ public List<Access> getAppListSorted() {
+ List<Access> accesses = getAppList();
+ // Sort the list of Access by recency. Most recent accesses first.
+ Collections.sort(accesses, Collections.reverseOrder(new Comparator<Access>() {
+ @Override
+ public int compare(Access access1, Access access2) {
+ return Long.compare(access1.accessFinishTime, access2.accessFinishTime);
+ }
+ }));
+ return accesses;
+ }
+
+ /**
+ * Creates a Access entry for the given PackageOps.
+ *
+ * This method examines the time interval of the PackageOps first. If the PackageOps is older
+ * than the designated interval, this method ignores the PackageOps object and returns null.
+ * When the PackageOps is fresh enough, this method returns a Access object for the package
+ */
+ private Access getAccessFromOps(long now,
+ AppOpsManager.PackageOps ops) {
+ String packageName = ops.getPackageName();
+ List<AppOpsManager.OpEntry> entries = ops.getOps();
+ long locationAccessFinishTime = 0L;
+ // Earliest time for a location access to end and still be shown in list.
+ long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+ for (AppOpsManager.OpEntry entry : entries) {
+ locationAccessFinishTime = Math.max(entry.getLastAccessBackgroundTime(),
+ entry.getLastAccessForegroundTime());
+ }
+ // Bail out if the entry is out of date.
+ if (locationAccessFinishTime < recentLocationCutoffTime) {
+ return null;
+ }
+
+ // The package is fresh enough, continue.
+ int uid = ops.getUid();
+ int userId = UserHandle.getUserId(uid);
+
+ Access access = null;
+ try {
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.GET_META_DATA, userId);
+ if (appInfo == null) {
+ Log.w(TAG, "Null application info retrieved for package " + packageName
+ + ", userId " + userId);
+ return null;
+ }
+
+ final UserHandle userHandle = new UserHandle(userId);
+ Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId);
+ CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
+ CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
+ if (appLabel.toString().contentEquals(badgedAppLabel)) {
+ // If badged label is not different from original then no need for it as
+ // a separate content description.
+ badgedAppLabel = null;
+ }
+ access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel,
+ locationAccessFinishTime);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "package name not found for " + packageName + ", userId " + userId);
+ }
+ return access;
+ }
+
+ public static class Access {
+ public final String packageName;
+ public final UserHandle userHandle;
+ public final Drawable icon;
+ public final CharSequence label;
+ public final CharSequence contentDescription;
+ public final long accessFinishTime;
+
+ private Access(String packageName, UserHandle userHandle, Drawable icon,
+ CharSequence label, CharSequence contentDescription,
+ long accessFinishTime) {
+ this.packageName = packageName;
+ this.userHandle = userHandle;
+ this.icon = icon;
+ this.label = label;
+ this.contentDescription = contentDescription;
+ this.accessFinishTime = accessFinishTime;
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 74e5bf5..1f7f4bc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -93,105 +92,23 @@
}
@Test
- public void updateConnectivity_nullWifiInfoWithMacRandomizationOff_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(null).when(mWifiManager).getConnectionInfo();
-
+ public void updateConnectivity_null_setMacUnavailable() {
+ doReturn(null).when(mWifiManager).getFactoryMacAddresses();
mController.displayPreference(mScreen);
-
assertThat(mPreference.getSummary())
.isEqualTo(mContext.getString(R.string.status_unavailable));
}
@Test
- public void updateConnectivity_nullMacWithMacRandomizationOff_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(null).when(mWifiInfo).getMacAddress();
-
+ public void updateConnectivity_validMac_setValidMac() {
+ final String[] macAddresses = new String[]{TEST_MAC_ADDRESS};
+ doReturn(macAddresses).when(mWifiManager).getFactoryMacAddresses();
mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
-
- @Test
- public void updateConnectivity_defaultMacWithMacRandomizationOff_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
-
- @Test
- public void updateConnectivity_validMacWithMacRandomizationOff_setValidMac() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.OFF);
- doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
- }
- @Test
- public void updateConnectivity_nullWifiInfoWithMacRandomizationOn_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(null).when(mWifiManager).getConnectionInfo();
- mController.displayPreference(mScreen);
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
- @Test
- public void updateConnectivity_nullMacWithMacRandomizationOn_setMacUnavailable() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(null).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.status_unavailable));
- }
-
- @Test
- public void updateConnectivity_defaultMacWithMacRandomizationOn_setMacRandomized() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.wifi_status_mac_randomized));
- }
-
- @Test
- public void updateConnectivity_validMacWithMacRandomizationOn_setValidMac() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
- AbstractWifiMacAddressPreferenceController.ON);
- doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
}
private static class ConcreteWifiMacAddressPreferenceController
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2e107a..ad44b9a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -134,6 +134,7 @@
],
platform_apis: true,
+ product_specific: true,
certificate: "platform",
privileged: true,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index 88b8dd8..fbd863d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -11,13 +11,12 @@
* 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
+ * limitations under the License.
*/
package com.android.systemui.plugins;
-import android.hardware.Sensor;
-import android.hardware.TriggerEventListener;
+import android.hardware.SensorListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -31,26 +30,30 @@
int VERSION = 1;
/**
- * Registers for trigger events from the sensor. Trigger events are one-shot and need to
- * re-registered in order for them to be fired again.
+ * Registers for sensor events. Events will be sent until the listener is unregistered.
* @param sensor
* @param listener
- * @see android.hardware.SensorManager#requestTriggerSensor(
- * android.hardware.TriggerEventListener, android.hardware.Sensor)
+ * @see android.hardware.SensorManager#registerListener(SensorListener, int)
*/
- void registerTriggerEvent(Sensor sensor, TriggerEventListener listener);
+ void registerListener(Sensor sensor, SensorEventListener listener);
/**
- * Unregisters trigger events from the sensor.
+ * Unregisters events from the sensor.
* @param sensor
* @param listener
*/
- void unregisterTriggerEvent(Sensor sensor, TriggerEventListener listener);
+ void unregisterListener(Sensor sensor, SensorEventListener listener);
- interface TriggerEventListener {
- void onTrigger(TriggerEvent event);
+ /**
+ * Listener triggered whenever the Sensor has new data.
+ */
+ interface SensorEventListener {
+ void onSensorChanged(SensorEvent event);
}
+ /**
+ * Sensor that can be defined in a plugin.
+ */
class Sensor {
public static final int TYPE_WAKE_LOCK_SCREEN = 1;
public static final int TYPE_WAKE_DISPLAY = 2;
@@ -67,29 +70,32 @@
}
}
- class TriggerEvent {
+ /**
+ * Event sent by a {@link Sensor}.
+ */
+ class SensorEvent {
Sensor mSensor;
int mVendorType;
float[] mValues;
/**
- * Creates a trigger event
+ * Creates a sensor event.
* @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
* @param vendorType The vendor type, which should be unique for each type of sensor,
* e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
*/
- public TriggerEvent(Sensor sensor, int vendorType) {
+ public SensorEvent(Sensor sensor, int vendorType) {
this(sensor, vendorType, null);
}
/**
- * Creates a trigger event
+ * Creates a sensor event.
* @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
* @param vendorType The vendor type, which should be unique for each type of sensor,
* e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
* @param values Values captured by the sensor.
*/
- public TriggerEvent(Sensor sensor, int vendorType, float[] values) {
+ public SensorEvent(Sensor sensor, int vendorType, float[] values) {
mSensor = sensor;
mVendorType = vendorType;
mValues = values;
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
deleted file mode 100644
index a72e9b8..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="29.5dp"
- android:viewportWidth="25.6"
- android:viewportHeight="23.6">
- <group
- android:translateX="0.8"
- android:translateY="-1.1">
- <path
- android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
- android:fillColor="#FFFFFFFF"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
deleted file mode 100644
index 53e4efc..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32.0dp"
- android:height="29.5dp"
- android:viewportWidth="25.6"
- android:viewportHeight="23.6">
- <group
- android:translateX="0.8"
- android:translateY="-0.9">
- <path
- android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
- android:fillColor="#4DFFFFFF"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
deleted file mode 100644
index 8294183..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="29.5dp"
- android:viewportWidth="25.6"
- android:viewportHeight="23.6">
- <group
- android:translateX="0.8"
- android:translateY="-0.9">
- <path
- android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
- android:fillColor="#4DFFFFFF"/>
- <path
- android:pathData="M12.82,21.6l5.11,-6.36A8.942,8.942 0,0 0,12 13c-2.28,0 -4.35,0.85 -5.94,2.25l5.1,6.35c0.43,0.53 1.23,0.53 1.66,0z"
- android:fillColor="#FFFFFFFF"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
deleted file mode 100644
index 3d59cf2..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="29.5dp"
- android:viewportWidth="25.6"
- android:viewportHeight="23.6">
- <group
- android:translateX="0.8"
- android:translateY="-0.9">
- <path
- android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
- android:fillColor="#4DFFFFFF"/>
- <path
- android:pathData="M12.82,21.6l6.99,-8.7C17.71,11.1 14.99,10 12,10c-2.99,0 -5.72,1.1 -7.82,2.91l6.98,8.7c0.43,0.52 1.23,0.52 1.66,-0.01z"
- android:fillColor="#FFFFFFFF"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
deleted file mode 100644
index 21313b8..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="29.5dp"
- android:viewportWidth="25.6"
- android:viewportHeight="23.6">
- <group
- android:translateX="0.8"
- android:translateY="-0.9">
- <path
- android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
- android:fillColor="#4DFFFFFF"/>
- <path
- android:pathData="M12.82,21.6l8.25,-10.26A13.961,13.961 0,0 0,12 8c-3.46,0 -6.63,1.26 -9.07,3.35l8.23,10.26c0.43,0.52 1.23,0.52 1.66,-0.01z"
- android:fillColor="#FFFFFFFF"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
deleted file mode 100644
index fd763ff..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="29.5dp"
- android:viewportWidth="25.6"
- android:viewportHeight="23.6">
- <group
- android:translateX="0.8"
- android:translateY="-0.9">
- <path
- android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
- android:fillColor="#FFFFFFFF"/>
- </group>
-</vector>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index eda3c59..4fb1bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -44,8 +44,7 @@
public static final int PULSE_REASON_SENSOR_PICKUP = 3;
public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
- public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 6;
- public static final int REASON_SENSOR_WAKE_UP = 7;
+ public static final int REASON_SENSOR_WAKE_UP = 6;
private static boolean sRegisterKeyguardCallback = true;
@@ -212,7 +211,6 @@
case PULSE_REASON_SENSOR_PICKUP: return "pickup";
case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
- case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakeLockScreen";
case REASON_SENSOR_WAKE_UP: return "wakeup";
default: throw new IllegalArgumentException("bad reason: " + pulseReason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 7e77843..c2676d0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -17,7 +17,6 @@
package com.android.systemui.doze;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
-import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
import android.annotation.AnyThread;
import android.app.ActivityManager;
@@ -26,7 +25,6 @@
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
-import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
@@ -114,14 +112,7 @@
DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */),
- new PluginTriggerSensor(
- new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
- Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
- true /* configured */,
- DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
- false /* reports touch coordinates */,
- false /* touchscreen */),
- new PluginTriggerSensor(
+ new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
true /* configured */,
@@ -272,7 +263,7 @@
}
@Override
- public void onSensorChanged(SensorEvent event) {
+ public void onSensorChanged(android.hardware.SensorEvent event) {
if (DEBUG) Log.d(TAG, "onSensorChanged " + event);
mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
@@ -417,7 +408,7 @@
protected String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
- final StringBuilder sb = new StringBuilder("TriggerEvent[")
+ final StringBuilder sb = new StringBuilder("SensorEvent[")
.append(event.timestamp).append(',')
.append(event.sensor.getName());
if (event.values != null) {
@@ -432,23 +423,19 @@
/**
* A Sensor that is injected via plugin.
*/
- private class PluginTriggerSensor extends TriggerSensor {
+ private class PluginSensor extends TriggerSensor {
private final SensorManagerPlugin.Sensor mPluginSensor;
- private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> {
+ private final SensorManagerPlugin.SensorEventListener mTriggerEventListener = (event) -> {
DozeLog.traceSensor(mContext, mPulseReason);
mHandler.post(mWakeLock.wrap(() -> {
- if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
- mRegistered = false;
+ if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event));
mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
event.getValues());
- if (!mRegistered) {
- updateListener(); // reregister, this sensor only fires once
- }
}));
};
- PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
+ PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
super(null, setting, configured, pulseReason, reportsTouchCoordinates,
requiresTouchscreen);
@@ -460,13 +447,13 @@
if (!mConfigured) return;
AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager;
if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) {
- asyncSensorManager.requestPluginTriggerSensor(mPluginSensor, mTriggerEventListener);
+ asyncSensorManager.registerPluginListener(mPluginSensor, mTriggerEventListener);
mRegistered = true;
- if (DEBUG) Log.d(TAG, "requestPluginTriggerSensor");
+ if (DEBUG) Log.d(TAG, "registerPluginListener");
} else if (mRegistered) {
- asyncSensorManager.cancelPluginTriggerSensor(mPluginSensor, mTriggerEventListener);
+ asyncSensorManager.unregisterPluginListener(mPluginSensor, mTriggerEventListener);
mRegistered = false;
- if (DEBUG) Log.d(TAG, "cancelPluginTriggerSensor");
+ if (DEBUG) Log.d(TAG, "unregisterPluginListener");
}
}
@@ -479,7 +466,7 @@
.append(", mSensor=").append(mPluginSensor).append("}").toString();
}
- private String triggerEventToString(SensorManagerPlugin.TriggerEvent event) {
+ private String triggerEventToString(SensorManagerPlugin.SensorEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
.append(event.getSensor()).append(',')
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index afe9a74..1da8976 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -53,6 +53,12 @@
/** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
+ /**
+ * Last value sent by the wake-display sensor.
+ * Assuming that the screen should start on.
+ */
+ private static boolean sWakeDisplaySensorState = true;
+
private final Context mContext;
private final DozeMachine mMachine;
private final DozeSensors mDozeSensors;
@@ -128,7 +134,6 @@
boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
- boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
@@ -145,14 +150,6 @@
if (isDoubleTap) {
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
- } else if (isWakeLockScreen) {
- if (wakeEvent) {
- mDozeHost.setPassiveInterrupt(true);
- mMachine.wakeUp();
- DozeLog.traceLockScreenWakeUp(wakeEvent);
- } else {
- if (DEBUG) Log.d(TAG, "Unpulsing");
- }
} else if (isPickup) {
mDozeHost.setPassiveInterrupt(true);
mMachine.wakeUp();
@@ -199,6 +196,7 @@
DozeMachine.State state = mMachine.getState();
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+ sWakeDisplaySensorState = wake;
if (wake) {
proximityCheckThenCall((result) -> {
@@ -234,6 +232,9 @@
}
mDozeSensors.setListening(true);
mDozeHost.setPassiveInterrupt(false);
+ if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
+ onWakeScreen(false);
+ }
break;
case DOZE_AOD_PAUSED:
case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java b/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java
new file mode 100644
index 0000000..ebfafce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java
@@ -0,0 +1,121 @@
+/*
+ * 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.doze;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.SensorManagerPlugin;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.util.AsyncSensorManager;
+
+/**
+ * Controller responsible for waking up or making the device sleep based on ambient sensors.
+ */
+public class LockScreenWakeUpController implements StatusBarStateController.StateListener,
+ SensorManagerPlugin.SensorEventListener {
+
+ private static final String TAG = LockScreenWakeUpController.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final AsyncSensorManager mAsyncSensorManager;
+ private final SensorManagerPlugin.Sensor mSensor;
+ private final AmbientDisplayConfiguration mAmbientConfiguration;
+ private final PowerManager mPowerManager;
+ private final DozeHost mDozeHost;
+ private final Handler mHandler;
+ private boolean mRegistered;
+ private boolean mDozing;
+
+ public LockScreenWakeUpController(Context context, DozeHost dozeHost) {
+ this(Dependency.get(AsyncSensorManager.class),
+ new SensorManagerPlugin.Sensor(SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN),
+ new AmbientDisplayConfiguration(context),
+ context.getSystemService(PowerManager.class),
+ dozeHost, Dependency.get(StatusBarStateController.class), new Handler());
+ }
+
+ @VisibleForTesting
+ public LockScreenWakeUpController(AsyncSensorManager asyncSensorManager,
+ SensorManagerPlugin.Sensor sensor, AmbientDisplayConfiguration ambientConfiguration,
+ PowerManager powerManager, DozeHost dozeHost,
+ StatusBarStateController statusBarStateController, Handler handler) {
+ mAsyncSensorManager = asyncSensorManager;
+ mSensor = sensor;
+ mAmbientConfiguration = ambientConfiguration;
+ mPowerManager = powerManager;
+ mDozeHost = dozeHost;
+ mHandler = handler;
+ statusBarStateController.addCallback(this);
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ boolean isLockScreen = newState == StatusBarState.KEYGUARD
+ || newState == StatusBarState.SHADE_LOCKED;
+
+ if (!mAmbientConfiguration.wakeLockScreenGestureEnabled(UserHandle.USER_CURRENT)) {
+ if (mRegistered) {
+ mAsyncSensorManager.unregisterPluginListener(mSensor, this);
+ mRegistered = false;
+ }
+ return;
+ }
+
+ if (isLockScreen && !mRegistered) {
+ mAsyncSensorManager.registerPluginListener(mSensor, this);
+ mRegistered = true;
+ } else if (!isLockScreen && mRegistered) {
+ mAsyncSensorManager.unregisterPluginListener(mSensor, this);
+ mRegistered = false;
+ }
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ mDozing = isDozing;
+ }
+
+ @Override
+ public void onSensorChanged(SensorManagerPlugin.SensorEvent event) {
+ mHandler.post(()-> {
+ float[] rawValues = event.getValues();
+ boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
+
+ DozeLog.traceLockScreenWakeUp(wakeEvent);
+ if (wakeEvent && mDozing) {
+ mDozeHost.setPassiveInterrupt(true);
+ if (DEBUG) Log.d(TAG, "Wake up.");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
+ } else if (!wakeEvent && !mDozing) {
+ if (DEBUG) Log.d(TAG, "Nap time.");
+ mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+ } else if (DEBUG) {
+ Log.d(TAG, "Skip sensor event. Wake? " + wakeEvent + " dozing: " + mDozing);
+ }
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index d8f7b71..6939ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.WifiIcons;
import java.util.List;
@@ -190,7 +191,7 @@
} else if (!state.value) {
state.slash.isSlashed = true;
state.state = Tile.STATE_INACTIVE;
- state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disabled);
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
state.label = r.getString(R.string.quick_settings_wifi_label);
} else if (wifiConnected) {
state.icon = ResourceIcon.get(cb.wifiSignalIconId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 67b077e..7c30e48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -76,11 +76,14 @@
private final Rect mLastDockedBounds = new Rect();
private boolean mQsCustomizing;
+ private final Context mContext;
+
public LightBarController(Context ctx) {
mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
mStatusBarIconController = Dependency.get(DarkIconDispatcher.class);
mBatteryController = Dependency.get(BatteryController.class);
mBatteryController.addCallback(this);
+ mContext = ctx;
}
public void setNavigationBar(LightBarTransitionsController navigationBar) {
@@ -217,8 +220,9 @@
private void updateNavigation() {
if (mNavigationBarController != null) {
- mNavigationBarController.setIconsDark(
- mNavigationLight, animateChange());
+ if (!NavBarTintController.isEnabled(mContext)) {
+ mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 57cc7d6..7876aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
+import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.MathUtils;
+import android.provider.Settings;
import android.util.TimeUtils;
import com.android.systemui.Dependency;
@@ -42,13 +46,14 @@
public class LightBarTransitionsController implements Dumpable, Callbacks,
StatusBarStateController.StateListener {
- public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
+ public static final int DEFAULT_TINT_ANIMATION_DURATION = 120;
private static final String EXTRA_DARK_INTENSITY = "dark_intensity";
private final Handler mHandler;
private final DarkIntensityApplier mApplier;
private final KeyguardMonitor mKeyguardMonitor;
private final StatusBarStateController mStatusBarStateController;
+ private NavBarTintController mColorAdaptionController;
private boolean mTransitionDeferring;
private long mTransitionDeferringStartTime;
@@ -67,6 +72,8 @@
}
};
+ private final Context mContext;
+
public LightBarTransitionsController(Context context, DarkIntensityApplier applier) {
mApplier = applier;
mHandler = new Handler();
@@ -76,6 +83,7 @@
.addCallback(this);
mStatusBarStateController.addCallback(this);
mDozeAmount = mStatusBarStateController.getDozeAmount();
+ mContext = context;
}
public void destroy(Context context) {
@@ -106,7 +114,7 @@
public void appTransitionCancelled() {
if (mTransitionPending && mTintChangePending) {
mTintChangePending = false;
- animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+ animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration());
}
mTransitionPending = false;
}
@@ -146,10 +154,19 @@
Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
mTransitionDeferringDuration);
} else {
- animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+ animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration());
}
}
+ public long getTintAnimationDuration() {
+ if (NavBarTintController.isEnabled(mContext)) {
+ return Math.max(Settings.Global.getInt(mContext.getContentResolver(),
+ NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION),
+ MIN_COLOR_ADAPT_TRANSITION_TIME);
+ }
+ return DEFAULT_TINT_ANIMATION_DURATION;
+ }
+
public float getCurrentDarkIntensity() {
return mDarkIntensity;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
new file mode 100644
index 0000000..9ecee18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -0,0 +1,208 @@
+/*
+ * 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.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+
+public class NavBarTintController {
+ public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition";
+ public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
+
+ private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread");
+ private Handler mColorAdaptionHandler;
+
+ // Poll time for each iteration to color sample
+ private static final int COLOR_ADAPTION_TIMEOUT = 300;
+
+ // Passing the threshold of this luminance value will make the button black otherwise white
+ private static final float LUMINANCE_THRESHOLD = 0.3f;
+
+ // The home button's icon is actually smaller than the button's size, the percentage will
+ // cut into the button's size to determine the icon size
+ private static final float PERCENTAGE_BUTTON_PADDING = 0.3f;
+
+ // The distance from the home button to color sample around
+ private static final int COLOR_SAMPLE_MARGIN = 20;
+
+ private boolean mRunning;
+
+ private final NavigationBarView mNavigationBarView;
+ private final LightBarTransitionsController mLightBarController;
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+ public NavBarTintController(NavigationBarView navigationBarView,
+ LightBarTransitionsController lightBarController) {
+ mNavigationBarView = navigationBarView;
+ mLightBarController = lightBarController;
+ }
+
+ public void start() {
+ if (!isEnabled(mNavigationBarView.getContext())) {
+ return;
+ }
+ if (mColorAdaptionHandler == null) {
+ mColorAdaptHandlerThread.start();
+ mColorAdaptionHandler = new Handler(mColorAdaptHandlerThread.getLooper());
+ }
+ mColorAdaptionHandler.removeCallbacksAndMessages(null);
+ mColorAdaptionHandler.post(this::updateTint);
+ mRunning = true;
+ }
+
+ public void end() {
+ if (mColorAdaptionHandler != null) {
+ mColorAdaptionHandler.removeCallbacksAndMessages(null);
+ }
+ mRunning = false;
+ }
+
+ public void stop() {
+ end();
+ if (mColorAdaptionHandler != null) {
+ mColorAdaptHandlerThread.quitSafely();
+ }
+ }
+
+ private void updateTint() {
+ int[] navPos = new int[2];
+ int[] butPos = new int[2];
+ if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
+ return;
+ }
+
+ // Determine the area of the home icon in the larger home button
+ mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos);
+ final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth();
+ final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight();
+ final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth);
+ final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight);
+ final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding,
+ navWidth + butPos[0] - xPadding, navHeight + butPos[1] - yPadding);
+ if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) {
+ scheduleColorAdaption();
+ return;
+ }
+ mNavigationBarView.getCurrentView().getLocationOnScreen(navPos);
+ homeButtonRect.offset(navPos[0], navPos[1]);
+
+ // Apply a margin area around the button region to sample the colors, crop from screenshot
+ final Rect cropRect = new Rect(homeButtonRect);
+ cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN);
+ if (cropRect.isEmpty()) {
+ scheduleColorAdaption();
+ return;
+ }
+
+ // Determine the size of the home area
+ Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN,
+ homeButtonRect.width() + COLOR_SAMPLE_MARGIN,
+ homeButtonRect.height() + COLOR_SAMPLE_MARGIN);
+
+ // Get the screenshot around the home button icon to determine the color
+ DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ mNavigationBarView.getContext().getDisplay().getRealMetrics(mDisplayMetrics);
+ final Bitmap hardBitmap = SurfaceControl
+ .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ mNavigationBarView.getContext().getDisplay().getRotation());
+ if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) {
+ final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top,
+ cropRect.width(), cropRect.height());
+ final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false);
+
+ // Get the luminance value to determine if the home button should be black or white
+ final int[] pixels = new int[softBitmap.getByteCount() / 4];
+ softBitmap.getPixels(pixels, 0, softBitmap.getWidth(), 0, 0, softBitmap.getWidth(),
+ softBitmap.getHeight());
+ float r = 0, g = 0, blue = 0;
+
+ int width = cropRect.width();
+ int total = 0;
+ for (int i = 0; i < pixels.length; i += 4) {
+ int x = i % width;
+ int y = i / width;
+ if (!homeArea.contains(x, y)) {
+ r += Color.red(pixels[i]);
+ g += Color.green(pixels[i]);
+ blue += Color.blue(pixels[i]);
+ total++;
+ }
+ }
+
+ r /= total;
+ g /= total;
+ blue /= total;
+
+ r = Math.max(Math.min(r / 255f, 1), 0);
+ g = Math.max(Math.min(g / 255f, 1), 0);
+ blue = Math.max(Math.min(blue / 255f, 1), 0);
+
+ if (r <= 0.03928) {
+ r /= 12.92;
+ } else {
+ r = (float) Math.pow((r + 0.055) / 1.055, 2.4);
+ }
+ if (g <= 0.03928) {
+ g /= 12.92;
+ } else {
+ g = (float) Math.pow((g + 0.055) / 1.055, 2.4);
+ }
+ if (blue <= 0.03928) {
+ blue /= 12.92;
+ } else {
+ blue = (float) Math.pow((blue + 0.055) / 1.055, 2.4);
+ }
+
+ if (r * 0.2126 + g * 0.7152 + blue * 0.0722 > LUMINANCE_THRESHOLD) {
+ // Black
+ mMainHandler.post(
+ () -> mLightBarController
+ .setIconsDark(true /* dark */, true /* animate */));
+ } else {
+ // White
+ mMainHandler.post(
+ () -> mLightBarController
+ .setIconsDark(false /* dark */, true /* animate */));
+ }
+ cropBitmap.recycle();
+ hardBitmap.recycle();
+ }
+ scheduleColorAdaption();
+ }
+
+ private void scheduleColorAdaption() {
+ mColorAdaptionHandler.removeCallbacksAndMessages(null);
+ if (!mRunning || !isEnabled(mNavigationBarView.getContext())) {
+ return;
+ }
+ mColorAdaptionHandler.postDelayed(this::updateTint, COLOR_ADAPTION_TIMEOUT);
+ }
+
+ public static boolean isEnabled(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1;
+ }
+}
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 ae0a14529..55655d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -851,6 +851,16 @@
if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
notifyNavigationBarScreenOn();
+
+ if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ // Enabled and screen is on, start it again if enabled
+ if (NavBarTintController.isEnabled(getContext())) {
+ mNavigationBarView.getColorAdaptionController().start();
+ }
+ } else {
+ // Screen off disable it
+ mNavigationBarView.getColorAdaptionController().end();
+ }
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
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 30e8409..6a7983a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -149,6 +149,7 @@
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
+ private NavBarTintController mColorAdaptionController;
private NavigationPrototypeController mPrototypeController;
private NavigationGestureAction[] mDefaultGestureMap;
private QuickScrubAction mQuickScrubAction;
@@ -277,6 +278,15 @@
public void onBackButtonVisibilityChanged(boolean visible) {
getBackButton().setVisibility(visible ? VISIBLE : GONE);
}
+
+ @Override
+ public void onColorAdaptChanged(boolean enabled) {
+ if (enabled) {
+ mColorAdaptionController.start();
+ } else {
+ mColorAdaptionController.end();
+ }
+ }
};
public NavigationBarView(Context context, AttributeSet attrs) {
@@ -334,6 +344,11 @@
mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
mPrototypeController.register();
mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
+ mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController());
+ }
+
+ public NavBarTintController getColorAdaptionController() {
+ return mColorAdaptionController;
}
public BarTransitions getBarTransitions() {
@@ -1097,6 +1112,7 @@
Dependency.get(PluginManager.class).addPluginListener(this,
NavGesture.class, false /* Only one */);
setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
+ mColorAdaptionController.start();
}
@Override
@@ -1107,6 +1123,7 @@
mGestureHelper.destroy();
}
mPrototypeController.unregister();
+ mColorAdaptionController.stop();
setUpSwipeUpOnboarding(false);
for (int i = 0; i < mButtonDispatchers.size(); ++i) {
mButtonDispatchers.valueAt(i).onDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index b11b6d4..40ac793 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -37,6 +37,7 @@
static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+ public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
@Retention(RetentionPolicy.SOURCE)
@IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
@@ -73,6 +74,7 @@
public void register() {
registerObserver(HIDE_BACK_BUTTON_SETTING);
registerObserver(GESTURE_MATCH_SETTING);
+ registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
}
/**
@@ -96,6 +98,9 @@
} else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
mListener.onBackButtonVisibilityChanged(
!getGlobalBool(HIDE_BACK_BUTTON_SETTING));
+ } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
+ mListener.onColorAdaptChanged(
+ NavBarTintController.isEnabled(mContext));
}
} catch (SettingNotFoundException e) {
e.printStackTrace();
@@ -138,5 +143,6 @@
public interface OnPrototypeChangedListener {
void onGestureRemap(@GestureAction int[] actions);
void onBackButtonVisibilityChanged(boolean visible);
+ void onColorAdaptChanged(boolean enabled);
}
}
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 a508f1b..75e5cba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -148,6 +148,7 @@
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.doze.DozeReceiver;
+import com.android.systemui.doze.LockScreenWakeUpController;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -581,6 +582,7 @@
protected NotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private boolean mPulsing;
+ private LockScreenWakeUpController mLockScreenWakeUpController;
@Override
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -984,6 +986,7 @@
for (int i = 0; i < pattern.length; i++) {
mCameraLaunchGestureVibePattern[i] = pattern[i];
}
+ mLockScreenWakeUpController = new LockScreenWakeUpController(mContext, mDozeServiceHost);
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -2227,6 +2230,11 @@
mNavigationBar.getBarTransitions().setAutoDim(false);
}
mHandler.removeCallbacks(mAutoDim);
+
+ // Do not dim the navigation buttons if the its tint is controlled by the bar's background
+ if (NavBarTintController.isEnabled(mContext)) {
+ return;
+ }
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index 6ee6cb2..53d0228 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -28,7 +28,6 @@
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.WifiTracker;
import com.android.settingslib.wifi.WifiTracker.WifiListener;
-import com.android.systemui.R;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -43,13 +42,7 @@
// network credentials. This is used by quick settings for secured networks.
private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
- private static final int[] ICONS = {
- R.drawable.ic_qs_wifi_full_0,
- R.drawable.ic_qs_wifi_full_1,
- R.drawable.ic_qs_wifi_full_2,
- R.drawable.ic_qs_wifi_full_3,
- R.drawable.ic_qs_wifi_full_4,
- };
+ private static final int[] ICONS = WifiIcons.WIFI_FULL_ICONS;
private final Context mContext;
private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
index 374408d..f629863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
@@ -32,19 +32,24 @@
R.drawable.stat_sys_wifi_signal_4_fully }
};
+ static final int[] WIFI_FULL_ICONS = {
+ com.android.internal.R.drawable.ic_wifi_signal_0,
+ com.android.internal.R.drawable.ic_wifi_signal_1,
+ com.android.internal.R.drawable.ic_wifi_signal_2,
+ com.android.internal.R.drawable.ic_wifi_signal_3,
+ com.android.internal.R.drawable.ic_wifi_signal_4
+ };
+
public static final int[][] QS_WIFI_SIGNAL_STRENGTH = {
{ R.drawable.ic_qs_wifi_0,
R.drawable.ic_qs_wifi_1,
R.drawable.ic_qs_wifi_2,
R.drawable.ic_qs_wifi_3,
R.drawable.ic_qs_wifi_4 },
- { R.drawable.ic_qs_wifi_full_0,
- R.drawable.ic_qs_wifi_full_1,
- R.drawable.ic_qs_wifi_full_2,
- R.drawable.ic_qs_wifi_full_3,
- R.drawable.ic_qs_wifi_full_4 }
+ WIFI_FULL_ICONS
};
+ public static final int QS_WIFI_DISABLED = com.android.internal.R.drawable.ic_wifi_signal_0;
static final int QS_WIFI_NO_NETWORK = R.drawable.ic_qs_wifi_no_network;
static final int WIFI_NO_NETWORK = R.drawable.stat_sys_wifi_signal_null;
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
index 0dd8937..88cbbb5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
@@ -76,8 +76,9 @@
}
@Override
- protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs,
- Handler handler, int maxReportLatencyUs, int reservedFlags) {
+ protected boolean registerListenerImpl(SensorEventListener listener,
+ Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs,
+ int reservedFlags) {
mHandler.post(() -> {
if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) {
Log.e(TAG, "Registering " + listener + " for " + sensor + " failed.");
@@ -146,23 +147,28 @@
* @param sensor
* @param listener
*/
- public void requestPluginTriggerSensor(SensorManagerPlugin.Sensor sensor,
- SensorManagerPlugin.TriggerEventListener listener) {
+ public void registerPluginListener(SensorManagerPlugin.Sensor sensor,
+ SensorManagerPlugin.SensorEventListener listener) {
if (mPlugins.isEmpty()) {
Log.w(TAG, "No plugins registered");
}
mHandler.post(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
- mPlugins.get(i).registerTriggerEvent(sensor, listener);
+ mPlugins.get(i).registerListener(sensor, listener);
}
});
}
- public void cancelPluginTriggerSensor(SensorManagerPlugin.Sensor sensor,
- SensorManagerPlugin.TriggerEventListener listener) {
+ /**
+ * Unregisters all sensors that match the give type for all plugins.
+ * @param sensor
+ * @param listener
+ */
+ public void unregisterPluginListener(SensorManagerPlugin.Sensor sensor,
+ SensorManagerPlugin.SensorEventListener listener) {
mHandler.post(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
- mPlugins.get(i).unregisterTriggerEvent(sensor, listener);
+ mPlugins.get(i).unregisterListener(sensor, listener);
}
});
}
@@ -185,7 +191,8 @@
}
@Override
- protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
+ protected void unregisterListenerImpl(SensorEventListener listener,
+ Sensor sensor) {
mHandler.post(() -> {
if (sensor == null) {
mInner.unregisterListener(listener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/LockScreenWakeUpControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/LockScreenWakeUpControllerTest.java
new file mode 100644
index 0000000..8963b59
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/LockScreenWakeUpControllerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.doze;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.PowerManager;
+import android.support.test.filters.SmallTest;
+
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.SensorManagerPlugin;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.util.AsyncSensorManager;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class LockScreenWakeUpControllerTest extends SysuiTestCase {
+
+ @Mock
+ private AsyncSensorManager mAsyncSensorManager;
+ @Mock
+ private SensorManagerPlugin.Sensor mSensor;
+ @Mock
+ private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private DozeHost mDozeHost;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private Handler mHandler;
+
+ private LockScreenWakeUpController mLockScreenWakeUpController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mHandler).post(any());
+
+ mLockScreenWakeUpController = new LockScreenWakeUpController(mAsyncSensorManager, mSensor,
+ mAmbientDisplayConfiguration, mPowerManager, mDozeHost, mStatusBarStateController,
+ mHandler);
+ }
+
+ @Test
+ public void testOnStateChanged_registersUnregistersListener() {
+ when(mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(anyInt())).thenReturn(true);
+ mLockScreenWakeUpController.onStateChanged(StatusBarState.KEYGUARD);
+ mLockScreenWakeUpController.onStateChanged(StatusBarState.SHADE);
+
+ verify(mAsyncSensorManager, times(1)).registerPluginListener(eq(mSensor),
+ eq(mLockScreenWakeUpController));
+
+ mLockScreenWakeUpController.onStateChanged(StatusBarState.SHADE);
+ verify(mAsyncSensorManager).unregisterPluginListener(eq(mSensor),
+ eq(mLockScreenWakeUpController));
+ }
+
+ @Test
+ public void testOnStateChanged_disabledSensor() {
+ when(mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(anyInt()))
+ .thenReturn(false);
+ mLockScreenWakeUpController.onStateChanged(StatusBarState.KEYGUARD);
+ mLockScreenWakeUpController.onStateChanged(StatusBarState.SHADE);
+
+ verify(mAsyncSensorManager, never()).registerPluginListener(eq(mSensor),
+ eq(mLockScreenWakeUpController));
+ }
+
+ @Test
+ public void testOnSensorChanged_postsToMainThread() {
+ SensorManagerPlugin.SensorEvent event = new SensorManagerPlugin.SensorEvent(mSensor, 0);
+ mLockScreenWakeUpController.onSensorChanged(event);
+
+ verify(mHandler).post(any());
+ }
+
+ @Test
+ public void testOnSensorChanged_wakeUpWhenDozing() {
+ SensorManagerPlugin.SensorEvent event =
+ new SensorManagerPlugin.SensorEvent(mSensor, 0, new float[] {1});
+ mLockScreenWakeUpController.onSensorChanged(event);
+ verify(mPowerManager, never()).wakeUp(anyLong(), any());
+
+ mLockScreenWakeUpController.onDozingChanged(true);
+ mLockScreenWakeUpController.onSensorChanged(event);
+ verify(mPowerManager).wakeUp(anyLong(), any());
+ }
+
+ @Test
+ public void testOnSensorChanged_sleepsWhenAwake() {
+ boolean[] goToSleep = new boolean[] {false};
+ doAnswer(invocation -> goToSleep[0] = true)
+ .when(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
+ SensorManagerPlugin.SensorEvent event =
+ new SensorManagerPlugin.SensorEvent(mSensor, 0, new float[] {0});
+ mLockScreenWakeUpController.onDozingChanged(true);
+ mLockScreenWakeUpController.onSensorChanged(event);
+ Assert.assertFalse("goToSleep should have never been called.", goToSleep[0]);
+
+ mLockScreenWakeUpController.onDozingChanged(false);
+ mLockScreenWakeUpController.onSensorChanged(event);
+ Assert.assertTrue("goToSleep should have been called.", goToSleep[0]);
+ }
+}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 66d64b1..529d78f 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6649,6 +6649,21 @@
// OS: Q
QS_SENSOR_PRIVACY = 1598;
+ // Tagged data for SMART_REPLY_VISIBLE. Count of number of smart actions.
+ // OS: Q
+ NOTIFICATION_SMART_ACTION_COUNT = 1599;
+
+ // Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION.
+ // Whether the notification has notification-assistant generated
+ // actions/replies.
+ // OS: Q
+ NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED = 1600;
+
+ // Tagged data for NOTIFICATION_ITEM_ACTION. Whether the action is a smart
+ // action.
+ // OS: Q
+ NOTIFICATION_ACTION_IS_SMART = 1601;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index a917ced..a533640 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,10 +16,13 @@
package com.android.server.backup;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
@@ -41,6 +44,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
@@ -83,22 +87,27 @@
}
private final Context mContext;
- private UserBackupManagerService mUserBackupManagerService;
+ private final Trampoline mTrampoline;
+ private final HandlerThread mBackupThread;
+
+ // Keeps track of all unlocked users registered with this service. Indexed by user id.
+ private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>();
+
+ private Set<ComponentName> mTransportWhitelist;
/** Instantiate a new instance of {@link BackupManagerService}. */
public BackupManagerService(
Context context, Trampoline trampoline, HandlerThread backupThread) {
- // Set up our transport options and initialize the default transport
- SystemConfig systemConfig = SystemConfig.getInstance();
- Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
- if (transportWhitelist == null) {
- transportWhitelist = Collections.emptySet();
- }
+ mContext = checkNotNull(context);
+ mTrampoline = checkNotNull(trampoline);
+ mBackupThread = checkNotNull(backupThread);
- mContext = context;
- mUserBackupManagerService =
- UserBackupManagerService.createAndInitializeService(
- context, trampoline, backupThread, transportWhitelist);
+ // Set up our transport options.
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
+ if (mTransportWhitelist == null) {
+ mTransportWhitelist = Collections.emptySet();
+ }
}
/**
@@ -115,12 +124,6 @@
}
}
- // TODO(b/118520567): Remove when tests are modified to use per-user instance.
- @VisibleForTesting
- void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
- mUserBackupManagerService = userBackupManagerService;
- }
-
/**
* Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
* a background thread to keep the unlock time down.
@@ -139,9 +142,42 @@
* Starts the backup service for user {@code userId} by creating a new instance of {@link
* UserBackupManagerService} and registering it with this service.
*/
- // TODO(b/120212806): Add UserBackupManagerService initialization logic.
- void startServiceForUser(int userId) {
- // Intentionally empty.
+ @VisibleForTesting
+ protected void startServiceForUser(int userId) {
+ UserBackupManagerService userBackupManagerService =
+ UserBackupManagerService.createAndInitializeService(
+ mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+ startServiceForUser(userId, userBackupManagerService);
+ }
+
+ /**
+ * Starts the backup service for user {@code userId} by registering its instance of {@link
+ * UserBackupManagerService} with this service.
+ */
+ void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+ mServiceUsers.put(userId, userBackupManagerService);
+ }
+
+ SparseArray<UserBackupManagerService> getServiceUsers() {
+ return mServiceUsers;
+ }
+
+ /**
+ * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+ * If the user is not registered with the service (either the user is locked or not eligible for
+ * the backup service) then return {@code null}.
+ *
+ * @param userId The id of the user to retrieve its instance of {@link
+ * UserBackupManagerService}.
+ * @param caller A {@link String} identifying the caller for logging purposes.
+ */
+ @Nullable
+ private UserBackupManagerService getServiceForUser(@UserIdInt int userId, String caller) {
+ UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
+ if (userBackupManagerService == null) {
+ Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+ }
+ return userBackupManagerService;
}
/*
@@ -149,7 +185,7 @@
* They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
* action on the passed in user. Currently this is a straight redirection (see TODO).
*/
- // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+ // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter
// ---------------------------------------------
// BACKUP AGENT OPERATIONS
@@ -161,7 +197,12 @@
* backup.
*/
public void dataChanged(String packageName) {
- mUserBackupManagerService.dataChanged(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "dataChanged()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dataChanged(packageName);
+ }
}
/**
@@ -169,7 +210,12 @@
* {@link ActivityManager}.
*/
public void agentConnected(String packageName, IBinder agentBinder) {
- mUserBackupManagerService.agentConnected(packageName, agentBinder);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "agentConnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentConnected(packageName, agentBinder);
+ }
}
/**
@@ -177,7 +223,12 @@
* called from the {@link ActivityManager}.
*/
public void agentDisconnected(String packageName) {
- mUserBackupManagerService.agentDisconnected(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "agentDisconnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentDisconnected(packageName);
+ }
}
/**
@@ -185,7 +236,12 @@
* outstanding asynchronous backup/restore operation.
*/
public void opComplete(int token, long result) {
- mUserBackupManagerService.opComplete(token, result);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "opComplete()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.opComplete(token, result);
+ }
}
// ---------------------------------------------
@@ -194,7 +250,12 @@
/** Run an initialize operation for the given transports {@code transportNames}. */
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
- mUserBackupManagerService.initializeTransports(transportNames, observer);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "initializeTransports()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.initializeTransports(transportNames, observer);
+ }
}
/**
@@ -202,35 +263,70 @@
* transportName}.
*/
public void clearBackupData(String transportName, String packageName) {
- mUserBackupManagerService.clearBackupData(transportName, packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "clearBackupData()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.clearBackupData(transportName, packageName);
+ }
}
/** Return the name of the currently active transport. */
+ @Nullable
public String getCurrentTransport() {
- return mUserBackupManagerService.getCurrentTransport();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransport();
}
/**
* Returns the {@link ComponentName} of the host service of the selected transport or {@code
* null} if no transport selected or if the transport selected is not registered.
*/
+ @Nullable
public ComponentName getCurrentTransportComponent() {
- return mUserBackupManagerService.getCurrentTransportComponent();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransportComponent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransportComponent();
}
/** Report all known, available backup transports by name. */
+ @Nullable
public String[] listAllTransports() {
- return mUserBackupManagerService.listAllTransports();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransports()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransports();
}
/** Report all known, available backup transports by {@link ComponentName}. */
+ @Nullable
public ComponentName[] listAllTransportComponents() {
- return mUserBackupManagerService.listAllTransportComponents();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransportComponents()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransportComponents();
}
/** Report all system whitelisted transports. */
+ @Nullable
public String[] getTransportWhitelist() {
- return mUserBackupManagerService.getTransportWhitelist();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getTransportWhitelist()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getTransportWhitelist();
}
/**
@@ -263,13 +359,18 @@
String currentDestinationString,
@Nullable Intent dataManagementIntent,
String dataManagementLabel) {
- mUserBackupManagerService.updateTransportAttributes(
- transportComponent,
- name,
- configurationIntent,
- currentDestinationString,
- dataManagementIntent,
- dataManagementLabel);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "updateTransportAttributes()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.updateTransportAttributes(
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
}
/**
@@ -281,7 +382,12 @@
@Deprecated
@Nullable
public String selectBackupTransport(String transportName) {
- return mUserBackupManagerService.selectBackupTransport(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.selectBackupTransport(transportName);
}
/**
@@ -290,7 +396,12 @@
*/
public void selectBackupTransportAsync(
ComponentName transportComponent, ISelectBackupTransportCallback listener) {
- mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransportAsync()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+ }
}
/**
@@ -298,8 +409,14 @@
* available transports, or if the transport does not supply any configuration UI, the method
* returns {@code null}.
*/
+ @Nullable
public Intent getConfigurationIntent(String transportName) {
- return mUserBackupManagerService.getConfigurationIntent(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getConfigurationIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getConfigurationIntent(transportName);
}
/**
@@ -311,21 +428,39 @@
* @param transportName The name of the registered transport.
* @return The current destination string or null if the transport is not registered.
*/
+ @Nullable
public String getDestinationString(String transportName) {
- return mUserBackupManagerService.getDestinationString(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getDestinationString()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDestinationString(transportName);
}
/** Supply the manage-data intent for the given transport. */
+ @Nullable
public Intent getDataManagementIntent(String transportName) {
- return mUserBackupManagerService.getDataManagementIntent(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementIntent(transportName);
}
/**
* Supply the menu label for affordances that fire the manage-data intent for the given
* transport.
*/
+ @Nullable
public String getDataManagementLabel(String transportName) {
- return mUserBackupManagerService.getDataManagementLabel(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementLabel()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementLabel(transportName);
}
// ---------------------------------------------
@@ -335,17 +470,32 @@
/** Enable/disable the backup service. This is user-configurable via backup settings. */
public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
- mUserBackupManagerService.setBackupEnabled(enable);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "setBackupEnabled()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setBackupEnabled(enable);
+ }
}
/** Enable/disable automatic restore of app data at install time. */
public void setAutoRestore(boolean autoRestore) {
- mUserBackupManagerService.setAutoRestore(autoRestore);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "setAutoRestore()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setAutoRestore(autoRestore);
+ }
}
/** Mark the backup service as having been provisioned (device has gone through SUW). */
public void setBackupProvisioned(boolean provisioned) {
- mUserBackupManagerService.setBackupProvisioned(provisioned);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "setBackupProvisioned()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setBackupProvisioned(provisioned);
+ }
}
/**
@@ -353,7 +503,10 @@
*/
public boolean isBackupEnabled(@UserIdInt int userId) {
enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
- return mUserBackupManagerService.isBackupEnabled();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "isBackupEnabled()");
+
+ return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
}
// ---------------------------------------------
@@ -362,14 +515,24 @@
/** Checks if the given package {@code packageName} is eligible for backup. */
public boolean isAppEligibleForBackup(String packageName) {
- return mUserBackupManagerService.isAppEligibleForBackup(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "isAppEligibleForBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.isAppEligibleForBackup(packageName);
}
/**
* Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
*/
+ @Nullable
public String[] filterAppsEligibleForBackup(String[] packages) {
- return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "filterAppsEligibleForBackup()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.filterAppsEligibleForBackup(packages);
}
/**
@@ -378,7 +541,12 @@
*/
public void backupNow(@UserIdInt int userId) {
enforceCallingPermissionOnUserId(userId, "backupNow");
- mUserBackupManagerService.backupNow();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "backupNow()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.backupNow();
+ }
}
/**
@@ -392,13 +560,23 @@
IBackupManagerMonitor monitor,
int flags) {
enforceCallingPermissionOnUserId(userId, "requestBackup");
- return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "requestBackup()");
+
+ return userBackupManagerService == null
+ ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
+ : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
}
/** Cancel all running backup operations. */
public void cancelBackups(@UserIdInt int userId) {
enforceCallingPermissionOnUserId(userId, "cancelBackups");
- mUserBackupManagerService.cancelBackups();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "cancelBackups()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.cancelBackups();
+ }
}
/**
@@ -410,7 +588,11 @@
* return value to the callback {@link JobService#onStartJob(JobParameters)}.
*/
public boolean beginFullBackup(FullBackupJob scheduledJob) {
- return mUserBackupManagerService.beginFullBackup(scheduledJob);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "beginFullBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.beginFullBackup(scheduledJob);
}
/**
@@ -418,14 +600,24 @@
* longer met for running the full backup job.
*/
public void endFullBackup() {
- mUserBackupManagerService.endFullBackup();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "endFullBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.endFullBackup();
+ }
}
/**
* Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
*/
public void fullTransportBackup(String[] packageNames) {
- mUserBackupManagerService.fullTransportBackup(packageNames);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "fullTransportBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.fullTransportBackup(packageNames);
+ }
}
// ---------------------------------------------
@@ -437,15 +629,26 @@
* called from the {@link PackageManager}.
*/
public void restoreAtInstall(String packageName, int token) {
- mUserBackupManagerService.restoreAtInstall(packageName, token);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "restoreAtInstall()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.restoreAtInstall(packageName, token);
+ }
}
/**
* Begin a restore for the specified package {@code packageName} using the specified transport
* {@code transportName}.
*/
+ @Nullable
public IRestoreSession beginRestoreSession(String packageName, String transportName) {
- return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "beginRestoreSession()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.beginRestoreSession(packageName, transportName);
}
/**
@@ -453,7 +656,12 @@
* the active set if possible, else the ancestral one. Returns zero if none available.
*/
public long getAvailableRestoreToken(String packageName) {
- return mUserBackupManagerService.getAvailableRestoreToken(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getAvailableRestoreToken()");
+
+ return userBackupManagerService == null
+ ? 0
+ : userBackupManagerService.getAvailableRestoreToken(packageName);
}
// ---------------------------------------------
@@ -462,12 +670,19 @@
/** Sets the backup password used when running adb backup. */
public boolean setBackupPassword(String currentPassword, String newPassword) {
- return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "setBackupPassword()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
}
/** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
public boolean hasBackupPassword() {
- return mUserBackupManagerService.hasBackupPassword();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "hasBackupPassword()");
+
+ return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
}
/**
@@ -477,6 +692,7 @@
* requires on-screen confirmation by the user.
*/
public void adbBackup(
+ @UserIdInt int userId,
ParcelFileDescriptor fd,
boolean includeApks,
boolean includeObbs,
@@ -487,17 +703,23 @@
boolean doCompress,
boolean doKeyValue,
String[] packageNames) {
- mUserBackupManagerService.adbBackup(
- fd,
- includeApks,
- includeObbs,
- includeShared,
- doWidgets,
- doAllApps,
- includeSystem,
- doCompress,
- doKeyValue,
- packageNames);
+ enforceCallingPermissionOnUserId(userId, "adbBackup");
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "adbBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.adbBackup(
+ fd,
+ includeApks,
+ includeObbs,
+ includeShared,
+ doWidgets,
+ doAllApps,
+ includeSystem,
+ doCompress,
+ doKeyValue,
+ packageNames);
+ }
}
/**
@@ -505,8 +727,14 @@
* is synchronous and does not return to the caller until the restore has been completed. It
* requires on-screen confirmation by the user.
*/
- public void adbRestore(ParcelFileDescriptor fd) {
- mUserBackupManagerService.adbRestore(fd);
+ public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
+ enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "adbRestore()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.adbRestore(fd);
+ }
}
/**
@@ -519,8 +747,13 @@
String currentPassword,
String encryptionPassword,
IFullBackupRestoreObserver observer) {
- mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
- token, allow, currentPassword, encryptionPassword, observer);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "acknowledgeAdbBackupOrRestore()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.acknowledgeAdbBackupOrRestore(
+ token, allow, currentPassword, encryptionPassword, observer);
+ }
}
// ---------------------------------------------
@@ -529,7 +762,12 @@
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mUserBackupManagerService.dump(fd, pw, args);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "dump()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dump(fd, pw, args);
+ }
}
private static boolean readBackupEnableState(int userId) {
@@ -587,7 +825,7 @@
if (userId == UserHandle.USER_SYSTEM) {
sInstance.initializeServiceAndUnlockSystemUser();
} else {
- sInstance.startServiceForUser(userId);
+ sInstance.unlockUser(userId);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index ed6ff9b..59b72f9 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -116,7 +116,7 @@
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
- protected boolean isMultiUserEnabled() {
+ private boolean isMultiUserEnabled() {
return Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.BACKUP_MULTI_USER_ENABLED,
@@ -145,6 +145,10 @@
return new BackupManagerService(mContext, this, mHandlerThread);
}
+ protected void postToHandler(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+
/**
* Initialize {@link BackupManagerService} if the backup service is not disabled. Only the
* system user can initialize the service.
@@ -174,14 +178,18 @@
* to initialize {@link BackupManagerService} and set backup state for the system user.
*/
void initializeServiceAndUnlockSystemUser() {
- mHandler.post(
+ postToHandler(
() -> {
+ // Initialize the backup service.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
initializeService(UserHandle.USER_SYSTEM);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ // Start the service for the system user.
BackupManagerService service = mService;
if (service != null) {
+ Slog.i(TAG, "Starting service for system user");
+ service.startServiceForUser(UserHandle.USER_SYSTEM);
Slog.i(TAG, "Unlocking system user");
service.unlockSystemUser();
}
@@ -195,20 +203,21 @@
*/
// TODO(b/120212806): Consolidate service start for system and non-system users when system
// user-only logic is removed.
- void startServiceForUser(int userId) {
+ void unlockUser(int userId) {
if (!isMultiUserEnabled()) {
Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
return;
}
- mHandler.post(
- () -> {
- BackupManagerService service = mService;
- if (service != null) {
- Slog.i(TAG, "Starting service for user: " + userId);
- service.startServiceForUser(userId);
- }
- });
+ postToHandler(() -> startServiceForUser(userId));
+ }
+
+ private void startServiceForUser(int userId) {
+ BackupManagerService service = mService;
+ if (service != null) {
+ Slog.i(TAG, "Starting service for user: " + userId);
+ service.startServiceForUser(userId);
+ }
}
/**
@@ -242,6 +251,7 @@
if (makeActive) {
mService = createBackupManagerService();
mSuppressFile.delete();
+ startServiceForUser(userId);
} else {
mService = null;
try {
@@ -389,14 +399,13 @@
backupNowForUser(binderGetCallingUserId());
}
- @Override
- public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
- boolean includeShared, boolean doWidgets, boolean allApps,
- boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames)
- throws RemoteException {
+ public void adbBackup(@UserIdInt int userId, ParcelFileDescriptor fd,
+ boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
+ boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue,
+ String[] packageNames) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.adbBackup(fd, includeApks, includeObbs, includeShared, doWidgets,
+ svc.adbBackup(userId, fd, includeApks, includeObbs, includeShared, doWidgets,
allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
}
}
@@ -410,10 +419,10 @@
}
@Override
- public void adbRestore(ParcelFileDescriptor fd) throws RemoteException {
+ public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.adbRestore(fd);
+ svc.adbRestore(userId, fd);
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 5220a59..796ef40 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -37,6 +37,7 @@
import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AppGlobals;
@@ -2423,9 +2424,9 @@
* return to the caller until the backup has been completed. It requires on-screen confirmation
* by the user.
*/
- public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
- boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
- boolean compress, boolean doKeyValue, String[] pkgList) {
+ public void adbBackup(ParcelFileDescriptor fd, boolean includeApks,
+ boolean includeObbs, boolean includeShared, boolean doWidgets, boolean doAllApps,
+ boolean includeSystem, boolean compress, boolean doKeyValue, String[] pkgList) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
final int callingUserHandle = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 08034f7..0fa996e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1123,8 +1123,6 @@
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
- // And send a TIME_TICK right now, since it is important to get the UI updated.
- mHandler.post(() -> getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
}
static final class InFlight {
@@ -1298,7 +1296,7 @@
mInjector.init();
synchronized (mLock) {
- mHandler = new AlarmHandler(Looper.myLooper());
+ mHandler = new AlarmHandler();
mConstants = new Constants(mHandler);
mNextWakeup = mNextNonWakeup = 0;
@@ -3050,6 +3048,9 @@
mNonInteractiveTime = dur;
}
}
+ // And send a TIME_TICK right now, since it is important to get the UI updated.
+ mHandler.post(() ->
+ getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
} else {
mNonInteractiveStartTime = nowELAPSED;
}
@@ -3838,7 +3839,8 @@
mWakeLock.setWorkSource(null);
}
- private class AlarmHandler extends Handler {
+ @VisibleForTesting
+ class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1;
public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
public static final int LISTENER_TIMEOUT = 3;
@@ -3847,8 +3849,8 @@
public static final int APP_STANDBY_PAROLE_CHANGED = 6;
public static final int REMOVE_FOR_STOPPED = 7;
- AlarmHandler(Looper looper) {
- super(looper);
+ AlarmHandler() {
+ super(Looper.myLooper());
}
public void postRemoveForStopped(int uid) {
@@ -3961,8 +3963,8 @@
final WorkSource workSource = null; // Let system take blame for time tick events.
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
- 0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource,
- null, Process.myUid(), "android");
+ 0, null, mTimeTickTrigger, "TIME_TICK", AlarmManager.FLAG_STANDALONE,
+ workSource, null, Process.myUid(), "android");
// Finally, remember when we set the tick alarm
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b6c4921..1ef3e94 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1684,7 +1684,9 @@
}
synchronized (mRecords) {
- mEmergencyNumberList = TelephonyManager.getDefault().getCurrentEmergencyNumberList();
+ TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ mEmergencyNumberList = tm.getCurrentEmergencyNumberList();
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index 90fe30c..a584914 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -123,9 +123,8 @@
* if the file is not available.
*/
public static String readCmdlineFromProcfs(int pid) {
- String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
- String cmdline = readFileContents(path);
- return cmdline != null ? cmdline : "";
+ final String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
+ return parseCmdlineFromProcfs(readFileContents(path));
}
private static String readFileContents(String path) {
@@ -210,6 +209,24 @@
return m.find() ? Long.parseLong(m.group(1)) * BYTES_IN_KILOBYTE : 0;
}
+
+ /**
+ * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
+ *
+ * Parsing is required to strip anything after first null byte.
+ */
+ @VisibleForTesting
+ static String parseCmdlineFromProcfs(String cmdline) {
+ if (cmdline == null) {
+ return "";
+ }
+ int firstNullByte = cmdline.indexOf("\0");
+ if (firstNullByte == -1) {
+ return cmdline;
+ }
+ return cmdline.substring(0, firstNullByte);
+ }
+
/**
* Returns whether per-app memcg is available on device.
*/
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 422f556..e40949b 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -217,6 +217,19 @@
@Override
// Called concurrently by multiple binder threads.
// This method must not block or perform long-running operations.
+ public synchronized void onNat64PrefixEvent(int netId,
+ boolean added, String prefixString, int prefixLength)
+ throws RemoteException {
+ for (INetdEventCallback callback : mNetdEventCallbackList) {
+ if (callback != null) {
+ callback.onNat64PrefixEvent(netId, added, prefixString, prefixLength);
+ }
+ }
+ }
+
+ @Override
+ // Called concurrently by multiple binder threads.
+ // This method must not block or perform long-running operations.
public synchronized void onPrivateDnsValidationEvent(int netId,
String ipAddress, String hostname, boolean validated)
throws RemoteException {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e7c3c7b..d96b6cb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,11 +1951,6 @@
}
// Native callback.
- private int getPointerDisplayId() {
- return mWindowManagerCallbacks.getPointerDisplayId();
- }
-
- // Native callback.
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
@@ -2022,8 +2017,6 @@
KeyEvent event, int policyFlags);
public int getPointerLayer();
-
- public int getPointerDisplayId();
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 28a6ba4..67293b9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -675,13 +675,6 @@
private final int mHardKeyboardBehavior;
/**
- * Whether we temporarily allow IMEs implemented in instant apps to run for testing.
- *
- * <p>Note: This is quite dangerous. Don't forget to reset after you finish testing.</p>
- */
- private boolean mBindInstantServiceAllowed = false;
-
- /**
* Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
* internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
* will not affect those tasks that are already posted.
@@ -1135,8 +1128,7 @@
final PackageManager pm = mContext.getPackageManager();
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
- getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
- getChangingUserId());
+ PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId());
// No need to lock this because we access it only on getRegisteredHandler().
if (!services.isEmpty()) {
mImePackageAppeared = true;
@@ -1684,9 +1676,6 @@
Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
return false;
}
- if (mBindInstantServiceAllowed) {
- flags |= Context.BIND_ALLOW_INSTANT;
- }
return mContext.bindServiceAsUser(service, conn, flags,
new UserHandle(mSettings.getCurrentUserId()));
}
@@ -3631,16 +3620,6 @@
return false;
}
- @PackageManager.ResolveInfoFlags
- private int getComponentMatchingFlags(@PackageManager.ResolveInfoFlags int baseFlags) {
- synchronized (mMethodMap) {
- if (mBindInstantServiceAllowed) {
- baseFlags |= PackageManager.MATCH_INSTANT;
- }
- return baseFlags;
- }
- }
-
@GuardedBy("mMethodMap")
void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
if (DEBUG) {
@@ -3664,8 +3643,7 @@
// services depending on the unlock state for the specified user.
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
- getComponentMatchingFlags(PackageManager.GET_META_DATA
- | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
+ PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
mSettings.getCurrentUserId());
final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
@@ -3707,8 +3685,7 @@
// conservative, but it seems we cannot use it for now (Issue 35176630).
final List<ResolveInfo> allInputMethodServices = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
- getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
- mSettings.getCurrentUserId());
+ PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getCurrentUserId());
final int N = allInputMethodServices.size();
for (int i = 0; i < N; ++i) {
final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
@@ -4606,8 +4583,7 @@
synchronized (mMethodMap) {
p.println("Current Input Method Manager state:");
int N = mMethodList.size();
- p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount
- + " mBindInstantServiceAllowed=" + mBindInstantServiceAllowed);
+ p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
for (int i=0; i<N; i++) {
InputMethodInfo info = mMethodList.get(i);
p.println(" InputMethod #" + i + ":");
@@ -4719,9 +4695,6 @@
if ("refresh_debug_properties".equals(cmd)) {
return refreshDebugProperties();
}
- if ("set-bind-instant-service-allowed".equals(cmd)) {
- return setBindInstantServiceAllowed();
- }
// For existing "adb shell ime <command>".
if ("ime".equals(cmd)) {
@@ -4752,12 +4725,6 @@
@BinderThread
@ShellCommandResult
- private int setBindInstantServiceAllowed() {
- return mService.handleSetBindInstantServiceAllowed(this);
- }
-
- @BinderThread
- @ShellCommandResult
private int refreshDebugProperties() {
DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
return ShellCommandResult.SUCCESS;
@@ -4774,9 +4741,6 @@
pw.println(" Synonym of dumpsys.");
pw.println(" ime <command> [options]");
pw.println(" Manipulate IMEs. Run \"ime help\" for details.");
- pw.println(" set-bind-instant-service-allowed true|false ");
- pw.println(" Set whether binding to services provided by instant apps is "
- + "allowed.");
}
}
@@ -4825,53 +4789,6 @@
// Shell command handlers:
/**
- * Handles {@code adb shell cmd input_method set-bind-instant-service-allowed}.
- *
- * @param shellCommand {@link ShellCommand} object that is handling this command.
- * @return Exit code of the command.
- */
- @BinderThread
- @RequiresPermission(android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
- @ShellCommandResult
- private int handleSetBindInstantServiceAllowed(@NonNull ShellCommand shellCommand) {
- final String allowedString = shellCommand.getNextArgRequired();
- if (allowedString == null) {
- shellCommand.getErrPrintWriter().println("Error: no true/false specified");
- return ShellCommandResult.FAILURE;
- }
- final boolean allowed = Boolean.parseBoolean(allowedString);
- synchronized (mMethodMap) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
- != PackageManager.PERMISSION_GRANTED) {
- shellCommand.getErrPrintWriter().print(
- "Caller must have MANAGE_BIND_INSTANT_SERVICE permission");
- return ShellCommandResult.FAILURE;
- }
-
- if (mBindInstantServiceAllowed == allowed) {
- // Nothing to do.
- return ShellCommandResult.SUCCESS;
- }
- mBindInstantServiceAllowed = allowed;
-
- // Rebuild everything.
- final long ident = Binder.clearCallingIdentity();
- try {
- // Reset the current IME
- resetSelectedInputMethodAndSubtypeLocked(null);
- // Also reset the settings of the current IME
- mSettings.putSelectedInputMethod(null);
- buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
- updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- return ShellCommandResult.SUCCESS;
- }
-
- /**
* Handles {@code adb shell ime list}.
* @param shellCommand {@link ShellCommand} object that is handling this command.
* @return Exit code of the command.
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
index ee1d847..827c0f1 100644
--- a/services/core/java/com/android/server/job/JobConcurrencyManager.java
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -44,17 +44,11 @@
* We manipulate this array until we arrive at what jobs should be running on
* what JobServiceContext.
*/
- JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+ JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
- /**
- * Indicates whether we need to act on this jobContext id
- */
- boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+ boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT];
- /**
- * The uid whose jobs we would like to assign to a context.
- */
- int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+ int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
JobConcurrencyManager(JobSchedulerService service) {
mService = service;
@@ -99,28 +93,30 @@
break;
}
- JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
- boolean[] act = mTmpAssignAct;
- int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
- int numActive = 0;
- int numForeground = 0;
+ // To avoid GC churn, we recycle the arrays.
+ JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
+ boolean[] slotChanged = mRecycledSlotChanged;
+ int[] preferredUidForContext = mRecycledPreferredUidForContext;
+
+ int numTotalRunningJobs = 0;
+ int numForegroundJobs = 0;
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
final JobServiceContext js = mService.mActiveServices.get(i);
final JobStatus status = js.getRunningJobLocked();
if ((contextIdToJobMap[i] = status) != null) {
- numActive++;
+ numTotalRunningJobs++;
if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
+ numForegroundJobs++;
}
}
- act[i] = false;
+ slotChanged[i] = false;
preferredUidForContext[i] = js.getPreferredUid();
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
}
for (int i=0; i<pendingJobs.size(); i++) {
- JobStatus nextPending = pendingJobs.get(i);
+ final JobStatus nextPending = pendingJobs.get(i);
// If job is already running, go to next job.
int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -131,23 +127,32 @@
final int priority = mService.evaluateJobPriorityLocked(nextPending);
nextPending.lastEvaluatedPriority = priority;
- // Find a context for nextPending. The context should be available OR
+ // Find an available slot for nextPending. The context should be available OR
// it should have lowest priority among all running jobs
// (sharing the same Uid as nextPending)
- int minPriority = Integer.MAX_VALUE;
- int minPriorityContextId = -1;
+ int minPriorityForPreemption = Integer.MAX_VALUE;
+ int selectedContextId = -1;
+ boolean startingJob = false;
for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
JobStatus job = contextIdToJobMap[j];
int preferredUid = preferredUidForContext[j];
if (job == null) {
- if ((numActive < mService.mMaxActiveJobs ||
- (priority >= JobInfo.PRIORITY_TOP_APP &&
- numForeground < mConstants.FG_JOB_COUNT)) &&
- (preferredUid == nextPending.getUid() ||
- preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+ final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs;
+ final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP)
+ && (numForegroundJobs < mConstants.FG_JOB_COUNT);
+ final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
+ || (preferredUid == JobServiceContext.NO_PREFERRED_UID);
+
+ // TODO: The following check is slightly wrong.
+ // Depending on how the pending jobs are sorted, we sometimes cap the total
+ // job count at mMaxActiveJobs (when all jobs are FG jobs), or
+ // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs
+ // and then FG_JOB_COUNT FG jobs.)
+ if ((totalCountOk || fgCountOk) && preferredUidOkay) {
// This slot is free, and we haven't yet hit the limit on
// concurrent jobs... we can just throw the job in to here.
- minPriorityContextId = j;
+ selectedContextId = j;
+ startingJob = true;
break;
}
// No job on this context, but nextPending can't run here because
@@ -158,30 +163,39 @@
if (job.getUid() != nextPending.getUid()) {
continue;
}
- if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
+
+ final int jobPriority = mService.evaluateJobPriorityLocked(job);
+ if (jobPriority >= nextPending.lastEvaluatedPriority) {
continue;
}
- if (minPriority > nextPending.lastEvaluatedPriority) {
- minPriority = nextPending.lastEvaluatedPriority;
- minPriorityContextId = j;
+
+ // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it)
+ if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) {
+ minPriorityForPreemption = nextPending.lastEvaluatedPriority;
+ selectedContextId = j;
+ // In this case, we're just going to preempt a low priority job, we're not
+ // actually starting a job, so don't set startingJob.
}
}
- if (minPriorityContextId != -1) {
- contextIdToJobMap[minPriorityContextId] = nextPending;
- act[minPriorityContextId] = true;
- numActive++;
+ if (selectedContextId != -1) {
+ contextIdToJobMap[selectedContextId] = nextPending;
+ slotChanged[selectedContextId] = true;
+ }
+ if (startingJob) {
+ // Increase the counters when we're going to start a job.
+ numTotalRunningJobs++;
if (priority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
+ numForegroundJobs++;
}
}
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
}
- tracker.noteConcurrency(numActive, numForeground);
+ tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs);
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
boolean preservePreferredUid = false;
- if (act[i]) {
+ if (slotChanged[i]) {
JobStatus js = activeServices.get(i).getRunningJobLocked();
if (js != null) {
if (DEBUG) {
@@ -195,7 +209,7 @@
final JobStatus pendingJob = contextIdToJobMap[i];
if (DEBUG) {
Slog.d(TAG, "About to run job on context "
- + String.valueOf(i) + ", job: " + pendingJob);
+ + i + ", job: " + pendingJob);
}
for (int ic=0; ic<controllers.size(); ic++) {
controllers.get(ic).prepareForExecutionLocked(pendingJob);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 10dc156..3f9d928 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1261,6 +1261,9 @@
@Override
public void onDeviceIdleStateChanged(boolean deviceIdle) {
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Doze state changed: " + deviceIdle);
+ }
if (deviceIdle) {
// When becoming idle, make sure no jobs are actively running,
// except those using the idle exemption flag.
@@ -1829,6 +1832,9 @@
}
} break;
case MSG_CHECK_JOB:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CHECK_JOB");
+ }
if (mReportedActive) {
// if jobs are currently being run, queue all ready jobs for execution.
queueReadyJobsForExecutionLocked();
@@ -1838,6 +1844,9 @@
}
break;
case MSG_CHECK_JOB_GREEDY:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
+ }
queueReadyJobsForExecutionLocked();
break;
case MSG_STOP_JOB:
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3d87634..bad259b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3131,7 +3131,11 @@
throws RemoteException {
Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
- Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+ if (automaticZenRule.getOwner() == null
+ && automaticZenRule.getConfigurationActivity() == null) {
+ throw new NullPointerException(
+ "Rule must have a conditionproviderservice and/or configuration activity");
+ }
Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
@@ -3144,7 +3148,11 @@
throws RemoteException {
Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
- Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+ if (automaticZenRule.getOwner() == null
+ && automaticZenRule.getConfigurationActivity() == null) {
+ throw new NullPointerException(
+ "Rule must have a conditionproviderservice and/or configuration activity");
+ }
Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
@@ -3178,6 +3186,16 @@
}
@Override
+ public void setAutomaticZenRuleState(String id, Condition condition) {
+ Preconditions.checkNotNull(id, "id is null");
+ Preconditions.checkNotNull(condition, "Condition is null");
+
+ enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
+
+ mZenModeHelper.setAutomaticZenRuleState(id, condition);
+ }
+
+ @Override
public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
enforcePolicyAccess(pkg, "setInterruptionFilter");
final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index b080a73..571f799 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -29,8 +29,11 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.Objects;
+/**
+ * Helper class for managing active rules from
+ * {@link android.service.notification.ConditionProviderService CPSes}.
+ */
public class ZenModeConditions implements ConditionProviders.Callback {
private static final String TAG = ZenModeHelper.TAG;
private static final boolean DEBUG = ZenModeHelper.DEBUG;
@@ -41,8 +44,6 @@
@VisibleForTesting
protected final ArrayMap<Uri, ComponentName> mSubscriptions = new ArrayMap<>();
- private boolean mFirstEvaluation = true;
-
public ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders) {
mHelper = helper;
mConditionProviders = conditionProviders;
@@ -73,8 +74,10 @@
final ArraySet<Uri> current = new ArraySet<>();
evaluateRule(config.manualRule, current, null, processSubscriptions);
for (ZenRule automaticRule : config.automaticRules.values()) {
- evaluateRule(automaticRule, current, trigger, processSubscriptions);
- updateSnoozing(automaticRule);
+ if (automaticRule.component != null) {
+ evaluateRule(automaticRule, current, trigger, processSubscriptions);
+ updateSnoozing(automaticRule);
+ }
}
synchronized (mSubscriptions) {
@@ -90,7 +93,6 @@
}
}
}
- mFirstEvaluation = false;
}
@Override
@@ -114,23 +116,14 @@
if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
ZenModeConfig config = mHelper.getConfig();
if (config == null) return;
- ComponentName trigger = null;
- boolean updated = updateCondition(id, condition, config.manualRule);
- for (ZenRule automaticRule : config.automaticRules.values()) {
- updated |= updateCondition(id, condition, automaticRule);
- updated |= updateSnoozing(automaticRule);
- if (updated) {
- trigger = automaticRule.component;
- }
- }
- if (updated) {
- mHelper.setConfig(config, trigger, "conditionChanged");
- }
+ mHelper.setAutomaticZenRuleState(id, condition);
}
+ // Only valid for CPS backed rules
private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger,
boolean processSubscriptions) {
if (rule == null || rule.conditionId == null) return;
+ if (rule.configurationActivity != null) return;
final Uri id = rule.conditionId;
boolean isSystemCondition = false;
for (SystemConditionProviderService sp : mConditionProviders.getSystemProviders()) {
@@ -140,6 +133,7 @@
isSystemCondition = true;
}
}
+ // ensure that we have a record of the rule if it's backed by an currently alive CPS
if (!isSystemCondition) {
final IConditionProvider cp = mConditionProviders.findConditionProvider(rule.component);
if (DEBUG) Log.d(TAG, "Ensure external rule exists: " + (cp != null) + " for " + id);
@@ -147,7 +141,8 @@
mConditionProviders.ensureRecordExists(rule.component, id, cp);
}
}
- if (rule.component == null) {
+ // empty rule? disable and bail early
+ if (rule.component == null && rule.enabler == null) {
Log.w(TAG, "No component found for automatic rule: " + rule.conditionId);
rule.enabled = false;
return;
@@ -155,6 +150,8 @@
if (current != null) {
current.add(id);
}
+
+ // If the rule is bound by a CPS and the CPS is alive, tell them about the rule
if (processSubscriptions && ((trigger != null && trigger.equals(rule.component))
|| isSystemCondition)) {
if (DEBUG) Log.d(TAG, "Subscribing to " + rule.component);
@@ -167,40 +164,20 @@
if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
}
}
- if (rule.condition == null) {
+ // backfill the rule state from CPS backed components if it's missing
+ if (rule.component != null && rule.condition == null) {
rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId);
if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: "
+ rule.conditionId);
}
}
- private boolean isAutomaticActive(ComponentName component) {
- if (component == null) return false;
- final ZenModeConfig config = mHelper.getConfig();
- if (config == null) return false;
- for (ZenRule rule : config.automaticRules.values()) {
- if (component.equals(rule.component) && rule.isAutomaticActive()) {
- return true;
- }
- }
- return false;
- }
-
private boolean updateSnoozing(ZenRule rule) {
- if (rule != null && rule.snoozing && (mFirstEvaluation || !rule.isTrueOrUnknown())) {
+ if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
rule.snoozing = false;
if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
return true;
}
return false;
}
-
- private boolean updateCondition(Uri id, Condition condition, ZenRule rule) {
- if (id == null || rule == null || rule.conditionId == null) return false;
- if (!rule.conditionId.equals(id)) return false;
- if (Objects.equals(condition, rule.condition)) return false;
- rule.condition = condition;
- return true;
- }
-
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 94d276c..f01d343 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -26,6 +26,8 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -282,20 +284,25 @@
public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
if (!isSystemRule(automaticZenRule)) {
- ServiceInfo owner = getServiceInfo(automaticZenRule.getOwner());
- if (owner == null) {
- throw new IllegalArgumentException("Owner is not a condition provider service");
+ PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
+ if (component == null) {
+ component = getActivityInfo(automaticZenRule.getConfigurationActivity());
}
-
+ if (component == null) {
+ throw new IllegalArgumentException("Lacking enabled CPS or config activity");
+ }
int ruleInstanceLimit = -1;
- if (owner.metaData != null) {
- ruleInstanceLimit = owner.metaData.getInt(
+ if (component.metaData != null) {
+ ruleInstanceLimit = component.metaData.getInt(
ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
}
- if (ruleInstanceLimit > 0 && ruleInstanceLimit
- < (getCurrentInstanceCount(automaticZenRule.getOwner()) + 1)) {
+ int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner())
+ + getCurrentInstanceCount(automaticZenRule.getConfigurationActivity())
+ + 1;
+ if (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount) {
throw new IllegalArgumentException("Rule instance limit exceeded");
}
+
}
ZenModeConfig newConfig;
@@ -377,11 +384,73 @@
}
}
- public int getCurrentInstanceCount(ComponentName owner) {
+ public void setAutomaticZenRuleState(String id, Condition condition) {
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return;
+
+ newConfig = mConfig.copy();
+ }
+ setAutomaticZenRuleState(newConfig, newConfig.automaticRules.get(id), condition);
+ }
+
+ public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition) {
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return;
+ newConfig = mConfig.copy();
+ }
+
+ setAutomaticZenRuleState(newConfig,
+ findMatchingRule(newConfig, ruleDefinition, condition),
+ condition);
+ }
+
+ private void setAutomaticZenRuleState(ZenModeConfig config, ZenRule rule, Condition condition) {
+ if (rule == null) return;
+
+ rule.condition = condition;
+ updateSnoozing(rule);
+ setConfigLocked(config, rule.component, "conditionChanged");
+ }
+
+ private ZenRule findMatchingRule(ZenModeConfig config, Uri id, Condition condition) {
+ if (ruleMatches(id, condition, config.manualRule)) {
+ return config.manualRule;
+ } else {
+ for (ZenRule automaticRule : config.automaticRules.values()) {
+ if (ruleMatches(id, condition, automaticRule)) {
+ return automaticRule;
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
+ if (id == null || rule == null || rule.conditionId == null) return false;
+ if (!rule.conditionId.equals(id)) return false;
+ if (Objects.equals(condition, rule.condition)) return false;
+ return true;
+ }
+
+ private boolean updateSnoozing(ZenRule rule) {
+ if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
+ rule.snoozing = false;
+ if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
+ return true;
+ }
+ return false;
+ }
+
+ public int getCurrentInstanceCount(ComponentName cn) {
+ if (cn == null) {
+ return 0;
+ }
int count = 0;
synchronized (mConfig) {
for (ZenRule rule : mConfig.automaticRules.values()) {
- if (rule.component != null && rule.component.equals(owner)) {
+ if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
count++;
}
}
@@ -401,7 +470,7 @@
if (packages != null) {
final int packageCount = packages.length;
for (int i = 0; i < packageCount; i++) {
- if (packages[i].equals(rule.component.getPackageName())) {
+ if (packages[i].equals(rule.pkg)) {
return true;
}
}
@@ -410,18 +479,6 @@
}
}
- // Checks zen rule properties are the same (doesn't check creation time, name nor enabled)
- // used to check if default rules were customized or not
- private boolean ruleValuesEqual(AutomaticZenRule rule, ZenRule defaultRule) {
- if (rule == null || defaultRule == null) {
- return false;
- }
- return rule.getInterruptionFilter() ==
- NotificationManager.zenModeToInterruptionFilter(defaultRule.zenMode)
- && rule.getConditionId().equals(defaultRule.conditionId)
- && rule.getOwner().equals(defaultRule.component);
- }
-
protected void updateDefaultZenRules() {
updateDefaultAutomaticRuleNames();
for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
@@ -443,7 +500,8 @@
}
private boolean isSystemRule(AutomaticZenRule rule) {
- return ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+ return rule.getOwner() != null
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
}
private ServiceInfo getServiceInfo(ComponentName owner) {
@@ -465,11 +523,31 @@
return null;
}
+ private ActivityInfo getActivityInfo(ComponentName configActivity) {
+ Intent queryIntent = new Intent();
+ queryIntent.setComponent(configActivity);
+ List<ResolveInfo> installedComponents = mPm.queryIntentActivitiesAsUser(
+ queryIntent,
+ PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA,
+ UserHandle.getCallingUserId());
+ if (installedComponents != null) {
+ for (int i = 0, count = installedComponents.size(); i < count; i++) {
+ ResolveInfo resolveInfo = installedComponents.get(i);
+ return resolveInfo.activityInfo;
+ }
+ }
+ return null;
+ }
+
private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
if (isNew) {
rule.id = ZenModeConfig.newRuleId();
rule.creationTime = System.currentTimeMillis();
rule.component = automaticZenRule.getOwner();
+ rule.configurationActivity = automaticZenRule.getConfigurationActivity();
+ rule.pkg = (rule.component != null)
+ ? rule.component.getPackageName()
+ : rule.configurationActivity.getPackageName();
}
if (rule.enabled != automaticZenRule.isEnabled()) {
@@ -488,14 +566,10 @@
}
protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
- if (rule.zenPolicy != null) {
- return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, rule.zenPolicy,
- rule.enabled, rule.creationTime);
- } else {
- return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
- NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
- rule.creationTime);
- }
+ return new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity,
+ rule.conditionId, rule.zenPolicy,
+ NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
+ rule.enabled, rule.creationTime);
}
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
@@ -697,8 +771,9 @@
ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) {
try {
- mPm.getPackageInfo(rule.component.getPackageName(),
- PackageManager.MATCH_ANY_USER);
+ if (rule.pkg != null) {
+ mPm.getPackageInfo(rule.pkg, PackageManager.MATCH_ANY_USER);
+ }
} catch (PackageManager.NameNotFoundException e) {
newConfig.automaticRules.removeAt(i);
}
@@ -753,11 +828,14 @@
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
- // may modify config
+ // handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
+
mConfigs.put(config.user, config);
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(reason, mConfig, config);
+
+ // send some broadcasts
final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
getNotificationPolicy(config));
if (!config.equals(mConfig)) {
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 6d59827..16143d3 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -60,7 +60,6 @@
boolean createIdmap(@NonNull final PackageInfo targetPackage,
@NonNull final PackageInfo overlayPackage, int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
if (DEBUG) {
Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
+ overlayPackage.packageName);
@@ -70,16 +69,19 @@
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
try {
if (FEATURE_FLAG_IDMAP2) {
- mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+ if (mIdmap2Service.verifyIdmap(overlayPath, userId)) {
+ return true;
+ }
+ return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null;
} else {
mInstaller.idmap(targetPath, overlayPath, sharedGid);
+ return true;
}
} catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
+ overlayPath + ": " + e.getMessage());
return false;
}
- return true;
}
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
@@ -88,15 +90,15 @@
}
try {
if (FEATURE_FLAG_IDMAP2) {
- mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+ return mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
} else {
mInstaller.removeIdmap(oi.baseCodePath);
+ return true;
}
} catch (Exception e) {
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
return false;
}
- return true;
}
boolean idmapExists(@NonNull final OverlayInfo oi) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d471904..a7f1146 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -223,8 +223,8 @@
public OverlayManagerService(@NonNull final Context context,
@NonNull final Installer installer) {
super(context);
- mSettingsFile =
- new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
+ mSettingsFile = new AtomicFile(
+ new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
mPackageManager = new PackageManagerHelper();
mUserManager = UserManagerService.getInstance();
IdmapManager im = new IdmapManager(installer);
@@ -717,9 +717,9 @@
final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
synchronized (mLock) {
final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
- final int N = targetPackageNames.size();
- for (int i = 0; i < N; i++) {
+ mImpl.getEnabledOverlayPackageNames("android", userId);
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
final String targetPackageName = targetPackageNames.get(i);
List<String> list = new ArrayList<>();
if (!"android".equals(targetPackageName)) {
@@ -730,8 +730,8 @@
}
}
- final int N = targetPackageNames.size();
- for (int i = 0; i < N; i++) {
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
final String targetPackageName = targetPackageNames.get(i);
if (DEBUG) {
Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
@@ -789,7 +789,7 @@
if (!mSettingsFile.getBaseFile().exists()) {
return;
}
- try (final FileInputStream stream = mSettingsFile.openRead()) {
+ try (FileInputStream stream = mSettingsFile.openRead()) {
mSettings.restore(stream);
// We might have data for dying users if the device was
@@ -915,8 +915,8 @@
if (!verbose) {
int count = 0;
- final int N = mCache.size();
- for (int i = 0; i < N; i++) {
+ final int n = mCache.size();
+ for (int i = 0; i < n; i++) {
final int userId = mCache.keyAt(i);
count += mCache.get(userId).size();
}
@@ -929,8 +929,8 @@
return;
}
- final int N = mCache.size();
- for (int i = 0; i < N; i++) {
+ final int n = mCache.size();
+ for (int i = 0; i < n; i++) {
final int userId = mCache.keyAt(i);
pw.println(TAB1 + "User " + userId);
final HashMap<String, PackageInfo> map = mCache.get(userId);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 112059d..b0d2704 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -55,8 +55,8 @@
*/
final class OverlayManagerServiceImpl {
// Flags to use in conjunction with updateState.
- private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
- private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
+ private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0;
+ private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1;
private final PackageManagerHelper mPackageManager;
private final IdmapManager mIdmapManager;
@@ -89,8 +89,8 @@
// a change in priority is only relevant for static RROs: specifically,
// a regular RRO should not have its state reset only because a change
// in priority
- if (theTruth.isStaticOverlayPackage() &&
- theTruth.overlayPriority != oldSettings.priority) {
+ if (theTruth.isStaticOverlayPackage()
+ && theTruth.overlayPriority != oldSettings.priority) {
return true;
}
return false;
@@ -294,8 +294,8 @@
final int userId, final int flags) {
boolean modified = false;
final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
- final int N = ois.size();
- for (int i = 0; i < N; i++) {
+ final int n = ois.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = ois.get(i);
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
userId);
@@ -610,8 +610,8 @@
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
userId);
final List<String> paths = new ArrayList<>(overlays.size());
- final int N = overlays.size();
- for (int i = 0; i < N; i++) {
+ final int n = overlays.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlays.get(i);
if (oi.isEnabled()) {
paths.add(oi.packageName);
@@ -632,8 +632,8 @@
userId);
// Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
- if (targetPackage != null && overlayPackage != null &&
- !("android".equals(targetPackageName)
+ if (targetPackage != null && overlayPackage != null
+ && !("android".equals(targetPackageName)
&& overlayPackage.isStaticOverlayPackage())) {
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
}
@@ -663,7 +663,7 @@
private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
@Nullable final PackageInfo overlayPackage, final int userId, final int flags)
- throws OverlayManagerSettings.BadKeyException {
+ throws OverlayManagerSettings.BadKeyException {
if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
return STATE_TARGET_UPGRADING;
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 572d368..ee06746 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -290,21 +290,22 @@
return;
}
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
+ final int n = mItems.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = mItems.get(i);
pw.println(item.mPackageName + ":" + item.getUserId() + " {");
pw.increaseIndent();
- pw.print("mPackageName.......: "); pw.println(item.mPackageName);
- pw.print("mUserId............: "); pw.println(item.getUserId());
- pw.print("mTargetPackageName.: "); pw.println(item.getTargetPackageName());
- pw.print("mBaseCodePath......: "); pw.println(item.getBaseCodePath());
- pw.print("mState.............: "); pw.println(OverlayInfo.stateToString(item.getState()));
- pw.print("mIsEnabled.........: "); pw.println(item.isEnabled());
- pw.print("mIsStatic..........: "); pw.println(item.isStatic());
- pw.print("mPriority..........: "); pw.println(item.mPriority);
- pw.print("mCategory..........: "); pw.println(item.mCategory);
+ pw.println("mPackageName.......: " + item.mPackageName);
+ pw.println("mUserId............: " + item.getUserId());
+ pw.println("mTargetPackageName.: " + item.getTargetPackageName());
+ pw.println("mBaseCodePath......: " + item.getBaseCodePath());
+ pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+ pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+ pw.println("mIsEnabled.........: " + item.isEnabled());
+ pw.println("mIsStatic..........: " + item.isStatic());
+ pw.println("mPriority..........: " + item.mPriority);
+ pw.println("mCategory..........: " + item.mCategory);
pw.decreaseIndent();
pw.println("}");
@@ -400,8 +401,8 @@
xml.startTag(null, TAG_OVERLAYS);
XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION);
- final int N = table.size();
- for (int i = 0; i < N; i++) {
+ final int n = table.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = table.get(i);
persistRow(xml, item);
}
@@ -542,8 +543,8 @@
}
private int select(@NonNull final String packageName, final int userId) {
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
+ final int n = mItems.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = mItems.get(i);
if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
return i;
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index d576d33..e2b1cba 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -121,8 +121,8 @@
for (final String targetPackageName : allOverlays.keySet()) {
out.println(targetPackageName);
List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName);
- final int N = overlaysForTarget.size();
- for (int i = 0; i < N; i++) {
+ final int n = overlaysForTarget.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlaysForTarget.get(i);
String status;
switch (oi.state) {
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
new file mode 100644
index 0000000..886cfb2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.Process;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides data to back {@code ModuleInfo} related APIs in the package manager. The data is stored
+ * as an XML resource in a configurable "module metadata" package.
+ */
+@VisibleForTesting
+public class ModuleInfoProvider {
+ private static final String TAG = "PackageManager.ModuleInfoProvider";
+
+ /**
+ * The key in the package's application level metadata bundle that provides a resource reference
+ * to the module metadata.
+ */
+ private static final String MODULE_METADATA_KEY = "android.content.pm.MODULE_METADATA";
+
+
+ private final Context mContext;
+ private final IPackageManager mPackageManager;
+ private final Map<String, ModuleInfo> mModuleInfo;
+
+ // TODO: Move this to an earlier boot phase if anybody requires it then.
+ private volatile boolean mMetadataLoaded;
+
+ ModuleInfoProvider(Context context, IPackageManager packageManager) {
+ mContext = context;
+ mPackageManager = packageManager;
+ mModuleInfo = new ArrayMap<>();
+ }
+
+ @VisibleForTesting
+ public ModuleInfoProvider(XmlResourceParser metadata, Resources resources) {
+ mContext = null;
+ mPackageManager = null;
+ mModuleInfo = new ArrayMap<>();
+ loadModuleMetadata(metadata, resources);
+ }
+
+ /** Called by the {@code PackageManager} when it has completed its boot sequence */
+ public void systemReady() {
+ final String packageName = mContext.getResources().getString(
+ R.string.config_defaultModuleMetadataProvider);
+ if (TextUtils.isEmpty(packageName)) {
+ Slog.w(TAG, "No configured module metadata provider.");
+ return;
+ }
+
+ final Resources packageResources;
+ final PackageInfo pi;
+ try {
+ pi = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_META_DATA, Process.SYSTEM_UID);
+
+ Context packageContext = mContext.createPackageContext(packageName, 0);
+ packageResources = packageContext.getResources();
+ } catch (RemoteException | NameNotFoundException e) {
+ Slog.w(TAG, "Unable to discover metadata package: " + packageName, e);
+ return;
+ }
+
+ XmlResourceParser parser = packageResources.getXml(
+ pi.applicationInfo.metaData.getInt(MODULE_METADATA_KEY));
+ loadModuleMetadata(parser, packageResources);
+ }
+
+ private void loadModuleMetadata(XmlResourceParser parser, Resources packageResources) {
+ try {
+ // The format for the module metadata is straightforward :
+ //
+ // The following attributes on <module> are currently defined :
+ // -- name : A resource reference to a User visible package name, maps to
+ // ModuleInfo#getName
+ // -- packageName : The package name of the module, see ModuleInfo#getPackageName
+ // -- isHidden : Whether the module is hidden, see ModuleInfo#isHidden
+ //
+ // <module-metadata>
+ // <module name="@string/resource" packageName="package_name" isHidden="false|true" />
+ // <module .... />
+ // </module-metadata>
+
+ XmlUtils.beginDocument(parser, "module-metadata");
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+
+ if (!"module".equals(parser.getName())) {
+ Slog.w(TAG, "Unexpected metadata element: " + parser.getName());
+ mModuleInfo.clear();
+ break;
+ }
+
+ // TODO: The module name here is fetched using the resource configuration applied
+ // at the time of parsing this information. This is probably not the best approach
+ // to dealing with this as we'll now have to listen to all config changes and
+ // regenerate the data if required. Also, is this the right way to parse a resource
+ // reference out of an XML file ?
+ final String moduleName = packageResources.getString(
+ Integer.parseInt(parser.getAttributeValue(null, "name").substring(1)));
+ final String modulePackageName = XmlUtils.readStringAttribute(parser,
+ "packageName");
+ final boolean isHidden = XmlUtils.readBooleanAttribute(parser, "isHidden");
+
+ ModuleInfo mi = new ModuleInfo();
+ mi.setHidden(isHidden);
+ mi.setPackageName(modulePackageName);
+ mi.setName(moduleName);
+
+ mModuleInfo.put(modulePackageName, mi);
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Slog.w(TAG, "Error parsing module metadata", e);
+ mModuleInfo.clear();
+ } finally {
+ parser.close();
+ mMetadataLoaded = true;
+ }
+ }
+
+ List<ModuleInfo> getInstalledModules(int flags) {
+ if (!mMetadataLoaded) {
+ throw new IllegalStateException("Call to getInstalledModules before metadata loaded");
+ }
+
+ return new ArrayList<>(mModuleInfo.values());
+ }
+
+ ModuleInfo getModuleInfo(String packageName, int flags) {
+ if (!mMetadataLoaded) {
+ throw new IllegalStateException("Call to getModuleInfo before metadata loaded");
+ }
+
+ return mModuleInfo.get(packageName);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2826e7b..28fb01d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -162,12 +162,14 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageList;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
+import android.content.pm.PackageManager.ModuleInfoFlags;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.CheckPermissionDelegate;
import android.content.pm.PackageManagerInternal.PackageListObserver;
@@ -718,6 +720,8 @@
private PackageManager mPackageManager;
+ private final ModuleInfoProvider mModuleInfoProvider;
+
class PackageParserCallback implements PackageParser.Callback {
@Override public final boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
@@ -3030,6 +3034,8 @@
} // synchronized (mPackages)
} // synchronized (mInstallLock)
+ mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
+
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
@@ -4969,6 +4975,16 @@
}
@Override
+ public ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) {
+ return mModuleInfoProvider.getModuleInfo(packageName, flags);
+ }
+
+ @Override
+ public List<ModuleInfo> getInstalledModules(int flags) {
+ return mModuleInfoProvider.getInstalledModules(flags);
+ }
+
+ @Override
public String[] getSystemSharedLibraryNames() {
// allow instant applications
synchronized (mPackages) {
@@ -20242,6 +20258,8 @@
}
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
}
+
+ mModuleInfoProvider.systemReady();
}
public void waitForAppDataPrepared() {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 519a20d..33a9650 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -16,9 +16,9 @@
package com.android.server.pm.dex;
+import android.os.Build;
import android.util.AtomicFile;
import android.util.Slog;
-import android.os.Build;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -26,26 +26,27 @@
import com.android.server.pm.AbstractStatsBase;
import com.android.server.pm.PackageManagerServiceUtils;
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collections;
-import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
-
/**
* Stat file which store usage information about dex files.
*/
@@ -86,6 +87,13 @@
private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
"=UnsupportedClassLoaderContext=";
+ /**
+ * Limit on how many secondary DEX paths we store for a single owner, to avoid one app causing
+ * unbounded memory consumption.
+ */
+ @VisibleForTesting
+ /* package */ static final int MAX_SECONDARY_FILES_PER_OWNER = 100;
+
// Map which structures the information we have on a package.
// Maps package name to package data (which stores info about UsedByOtherApps and
// secondary dex files.).
@@ -164,8 +172,12 @@
DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath);
if (existingData == null) {
// It's the first time we see this dex file.
- packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
- return true;
+ if (packageUseInfo.mDexUseInfoMap.size() < MAX_SECONDARY_FILES_PER_OWNER) {
+ packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
+ return true;
+ } else {
+ return updateLoadingPackages;
+ }
} else {
if (ownerUserId != existingData.mOwnerUserId) {
// Oups, this should never happen, the DexManager who calls this should
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 64ff9cf..2157c99 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -60,6 +60,7 @@
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
@@ -85,6 +86,7 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;
import android.view.IWindowManager;
@@ -120,12 +122,13 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Predicate;
public class WallpaperManagerService extends IWallpaperManager.Stub
implements IWallpaperManagerService {
- static final String TAG = "WallpaperManagerService";
- static final boolean DEBUG = false;
- static final boolean DEBUG_LIVE = DEBUG || true;
+ private static final String TAG = "WallpaperManagerService";
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_LIVE = true;
public static class Lifecycle extends SystemService {
private IWallpaperManagerService mService;
@@ -163,14 +166,14 @@
}
}
- final Object mLock = new Object();
+ private final Object mLock = new Object();
/**
* Minimum time between crashes of a wallpaper service for us to consider
* restarting it vs. just reverting to the static wallpaper.
*/
- static final long MIN_WALLPAPER_CRASH_TIME = 10000;
- static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
+ private static final long MIN_WALLPAPER_CRASH_TIME = 10000;
+ private static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
static final String WALLPAPER = "wallpaper_orig";
static final String WALLPAPER_CROP = "wallpaper";
static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
@@ -178,7 +181,7 @@
static final String WALLPAPER_INFO = "wallpaper_info.xml";
// All the various per-user state files we need to be aware of
- static final String[] sPerUserFiles = new String[] {
+ private static final String[] sPerUserFiles = new String[] {
WALLPAPER, WALLPAPER_CROP,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
WALLPAPER_INFO
@@ -335,7 +338,7 @@
}
}
- void notifyLockWallpaperChanged() {
+ private void notifyLockWallpaperChanged() {
final IWallpaperManagerCallback cb = mKeyguardListener;
if (cb != null) {
try {
@@ -487,7 +490,7 @@
boolean success = false;
// Only generate crop for default display.
- final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+ final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
Rect cropHint = new Rect(wallpaper.cropHint);
if (DEBUG) {
@@ -640,11 +643,11 @@
}
}
- final Context mContext;
- final IWindowManager mIWindowManager;
- final IPackageManager mIPackageManager;
- final MyPackageMonitor mMonitor;
- final AppOpsManager mAppOpsManager;
+ private final Context mContext;
+ private final IWindowManager mIWindowManager;
+ private final IPackageManager mIPackageManager;
+ private final MyPackageMonitor mMonitor;
+ private final AppOpsManager mAppOpsManager;
private final DisplayManager mDisplayManager;
private final DisplayManager.DisplayListener mDisplayListener =
@@ -654,11 +657,23 @@
public void onDisplayAdded(int displayId) {
synchronized (mLock) {
if (mLastWallpaper != null) {
- final WallpaperConnection.DisplayConnector connector =
- mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
- if (connector == null) return;
-
- connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+ if (supportsMultiDisplay(mLastWallpaper.connection)) {
+ final WallpaperConnection.DisplayConnector connector =
+ mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+ connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+ return;
+ }
+ // System wallpaper does not support multiple displays, attach this display to
+ // the fallback wallpaper.
+ if (mFallbackWallpaper != null) {
+ final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper
+ .connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+ connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
+ } else {
+ Slog.w(TAG, "No wallpaper can be added to the new display");
+ }
}
}
}
@@ -667,12 +682,19 @@
public void onDisplayRemoved(int displayId) {
synchronized (mLock) {
if (mLastWallpaper != null) {
- final WallpaperConnection.DisplayConnector connector =
- mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ WallpaperData targetWallpaper = null;
+ if (mLastWallpaper.connection.containsDisplay(displayId)) {
+ targetWallpaper = mLastWallpaper;
+ } else if (mFallbackWallpaper.connection.containsDisplay(displayId)) {
+ targetWallpaper = mFallbackWallpaper;
+ }
+ if (targetWallpaper == null) return;
+ WallpaperConnection.DisplayConnector connector =
+ targetWallpaper.connection.getDisplayConnectorOrCreate(displayId);
if (connector == null) return;
connector.disconnectLocked();
- mLastWallpaper.connection.removeDisplayConnector(displayId);
- mLastWallpaper.removeDisplayData(displayId);
+ targetWallpaper.connection.removeDisplayConnector(displayId);
+ removeDisplayData(displayId);
}
}
}
@@ -686,35 +708,40 @@
* Map of color listeners per user id.
* The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
*/
- final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners;
- WallpaperData mLastWallpaper;
- IWallpaperManagerCallback mKeyguardListener;
- boolean mWaitingForUnlock;
- boolean mShuttingDown;
+ private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+ mColorsChangedListeners;
+ private WallpaperData mLastWallpaper;
+ private IWallpaperManagerCallback mKeyguardListener;
+ private boolean mWaitingForUnlock;
+ private boolean mShuttingDown;
/**
* ID of the current wallpaper, changed every time anything sets a wallpaper.
* This is used for external detection of wallpaper update activity.
*/
- int mWallpaperId;
+ private int mWallpaperId;
/**
* Name of the component used to display bitmap wallpapers from either the gallery or
* built-in wallpapers.
*/
- final ComponentName mImageWallpaper;
+ private final ComponentName mImageWallpaper;
/**
* Name of the default wallpaper component; might be different from mImageWallpaper
*/
- final ComponentName mDefaultWallpaperComponent;
+ private final ComponentName mDefaultWallpaperComponent;
- final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
- final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
+ private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
+ private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
- final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
- int mCurrentUserId = UserHandle.USER_NULL;
- boolean mInAmbientMode;
+ private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
+
+ private WallpaperData mFallbackWallpaper;
+
+ private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private boolean mInAmbientMode;
static class WallpaperData {
@@ -780,18 +807,6 @@
private RemoteCallbackList<IWallpaperManagerCallback> callbacks
= new RemoteCallbackList<IWallpaperManagerCallback>();
- private static final class DisplayData {
- int mWidth = -1;
- int mHeight = -1;
- final Rect mPadding = new Rect(0, 0, 0, 0);
- final int mDisplayId;
-
- DisplayData(int displayId) {
- mDisplayId = displayId;
- }
- }
- private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
-
/**
* The crop hint supplied for displaying a subset of the source image
*/
@@ -812,24 +827,34 @@
boolean sourceExists() {
return wallpaperFile.exists();
}
+ }
- void removeDisplayData(int displayId) {
- mDisplayDatas.remove(displayId);
+ private static final class DisplayData {
+ int mWidth = -1;
+ int mHeight = -1;
+ final Rect mPadding = new Rect(0, 0, 0, 0);
+ final int mDisplayId;
+
+ DisplayData(int displayId) {
+ mDisplayId = displayId;
}
}
- private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
- WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+ private void removeDisplayData(int displayId) {
+ mDisplayDatas.remove(displayId);
+ }
+
+ private DisplayData getDisplayDataOrCreate(int displayId) {
+ DisplayData wpdData = mDisplayDatas.get(displayId);
if (wpdData == null) {
- wpdData = new WallpaperData.DisplayData(displayId);
+ wpdData = new DisplayData(displayId);
ensureSaneWallpaperDisplaySize(wpdData, displayId);
- data.mDisplayDatas.append(displayId, wpdData);
+ mDisplayDatas.append(displayId, wpdData);
}
return wpdData;
}
- private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
- int displayId) {
+ private void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
// We always want to have some reasonable width hint.
final int baseSize = getMaximumSizeDimension(displayId);
if (wpdData.mWidth < baseSize) {
@@ -842,12 +867,16 @@
private int getMaximumSizeDimension(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
+ display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ }
return display.getMaximumSizeDimension();
}
- void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
- for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
- final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+ void forEachDisplayData(Consumer<DisplayData> action) {
+ for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
+ final DisplayData wpdData = mDisplayDatas.valueAt(i);
action.accept(wpdData);
}
}
@@ -859,6 +888,36 @@
return mWallpaperId;
}
+ private boolean supportsMultiDisplay(WallpaperConnection connection) {
+ if (connection != null) {
+ return connection.mInfo == null // This is image wallpaper
+ || connection.mInfo.supportsMultipleDisplays();
+ }
+ return false;
+ }
+
+ private void updateFallbackConnection() {
+ if (mLastWallpaper == null || mFallbackWallpaper == null) return;
+ final WallpaperConnection systemConnection = mLastWallpaper.connection;
+ final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection;
+ if (supportsMultiDisplay(systemConnection)
+ && fallbackConnection.getConnectedEngineSize() != 0) {
+ fallbackConnection.forEachDisplayConnector(
+ WallpaperConnection.DisplayConnector::disconnectLocked);
+ fallbackConnection.mDisplayConnector.clear();
+ } else {
+ fallbackConnection.appendConnectorWithCondition(display ->
+ fallbackConnection.isUsableDisplay(display)
+ && display.getDisplayId() != DEFAULT_DISPLAY
+ && !fallbackConnection.containsDisplay(display.getDisplayId()));
+ fallbackConnection.forEachDisplayConnector(connector -> {
+ if (connector.mEngine == null) {
+ connector.connectLocked(fallbackConnection, mFallbackWallpaper);
+ }
+ });
+ }
+ }
+
class WallpaperConnection extends IWallpaperConnection.Stub
implements ServiceConnection {
@@ -877,8 +936,7 @@
}
void ensureStatusHandled() {
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
- mDisplayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
if (mDimensionsChanged) {
try {
mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
@@ -906,8 +964,7 @@
return;
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- mDisplayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
try {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
wpdData.mWidth, wpdData.mHeight,
@@ -982,19 +1039,33 @@
}
private void initDisplayState() {
- final Display[] displays = mDisplayManager.getDisplays();
- for (Display display : displays) {
- if (isUsableDisplay(display)) {
- final int displayId = display.getDisplayId();
- mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+ // Do not initialize fallback wallpaper
+ if (!mWallpaper.equals(mFallbackWallpaper)) {
+ if (supportsMultiDisplay(this)) {
+ // The system wallpaper is image wallpaper or it can supports multiple displays.
+ appendConnectorWithCondition(this::isUsableDisplay);
+ } else {
+ // The system wallpaper does not support multiple displays, so just attach it on
+ // default display.
+ mDisplayConnector.append(DEFAULT_DISPLAY,
+ new DisplayConnector(DEFAULT_DISPLAY));
}
}
}
- // TODO(b/115486823) Support the system decorations change at runtime.
+ private void appendConnectorWithCondition(Predicate<Display> tester) {
+ final Display[] displays = mDisplayManager.getDisplays();
+ for (Display display : displays) {
+ if (tester.test(display)) {
+ final int displayId = display.getDisplayId();
+ mDisplayConnector.append(displayId,
+ new DisplayConnector(displayId));
+ }
+ }
+ }
+
private boolean isUsableDisplay(Display display) {
return display != null && display.hasAccess(mClientUid)
- // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
&& (display.supportsSystemDecorations()
|| display.getDisplayId() == DEFAULT_DISPLAY);
}
@@ -1027,6 +1098,10 @@
return connector;
}
+ boolean containsDisplay(int displayId) {
+ return mDisplayConnector.get(displayId) != null;
+ }
+
void removeDisplayConnector(int displayId) {
final DisplayConnector connector = mDisplayConnector.get(displayId);
if (connector != null) {
@@ -1044,7 +1119,9 @@
// when we have an engine, but I'm not sure about
// locking there and anyway we always need to be able to
// recover if there is something wrong.
- saveSettingsLocked(mWallpaper.userId);
+ if (!mWallpaper.equals(mFallbackWallpaper)) {
+ saveSettingsLocked(mWallpaper.userId);
+ }
FgThread.getHandler().removeCallbacks(mResetRunnable);
}
}
@@ -1533,8 +1610,8 @@
// This corrects for mislabeling bugs that might have arisen from move-to
// operations involving the wallpaper files. This isn't timing-critical,
// so we do it in the background to avoid holding up the user unlock operation.
- if (mUserRestorecon.get(userId) != Boolean.TRUE) {
- mUserRestorecon.put(userId, Boolean.TRUE);
+ if (!mUserRestorecon.get(userId)) {
+ mUserRestorecon.put(userId, true);
Runnable relabeler = new Runnable() {
@Override
public void run() {
@@ -1562,7 +1639,7 @@
for (String filename : sPerUserFiles) {
new File(wallpaperDir, filename).delete();
}
- mUserRestorecon.remove(userId);
+ mUserRestorecon.delete(userId);
}
}
@@ -1789,7 +1866,7 @@
throw new IllegalArgumentException("Cannot find display with id=" + displayId);
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
if (width != wpdData.mWidth || height != wpdData.mHeight) {
wpdData.mWidth = width;
wpdData.mHeight = height;
@@ -1826,8 +1903,7 @@
}
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
return wpdData.mWidth;
} else {
return 0;
@@ -1845,8 +1921,7 @@
}
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
return wpdData.mHeight;
} else {
return 0;
@@ -1872,7 +1947,7 @@
throw new IllegalArgumentException("padding must be positive: " + padding);
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ final DisplayData wpdData = getDisplayDataOrCreate(displayId);
if (!padding.equals(wpdData.mPadding)) {
wpdData.mPadding.set(padding);
if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
@@ -1940,8 +2015,7 @@
return null;
}
// Only for default display.
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- DEFAULT_DISPLAY);
+ final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
try {
if (outParams != null) {
outParams.putInt("width", wpdData.mWidth);
@@ -2173,14 +2247,8 @@
// We know a-priori that there is no lock-only wallpaper currently
WallpaperData lockWP = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
- final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
- DEFAULT_DISPLAY);
- final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
- DEFAULT_DISPLAY);
lockWP.wallpaperId = sysWP.wallpaperId;
lockWP.cropHint.set(sysWP.cropHint);
- lockWPDData.mWidth = sysWPDData.mWidth;
- lockWPDData.mHeight = sysWPDData.mHeight;
lockWP.allowBackup = sysWP.allowBackup;
lockWP.primaryColors = sysWP.primaryColors;
@@ -2320,7 +2388,7 @@
return false;
}
- boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
+ private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
if (DEBUG_LIVE) {
Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
@@ -2443,15 +2511,17 @@
Slog.w(TAG, msg);
return false;
}
- if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
+ if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
+ && !wallpaper.equals(mFallbackWallpaper)) {
detachWallpaperLocked(mLastWallpaper);
}
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
newConn.mReply = reply;
- if (wallpaper.userId == mCurrentUserId) {
+ if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
mLastWallpaper = wallpaper;
}
+ updateFallbackConnection();
} catch (RemoteException e) {
String msg = "Remote exception for " + componentName + "\n" + e;
if (fromUser) {
@@ -2463,7 +2533,7 @@
return true;
}
- void detachWallpaperLocked(WallpaperData wallpaper) {
+ private void detachWallpaperLocked(WallpaperData wallpaper) {
if (wallpaper.connection != null) {
if (wallpaper.connection.mReply != null) {
try {
@@ -2473,7 +2543,8 @@
wallpaper.connection.mReply = null;
}
mContext.unbindService(wallpaper.connection);
- wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
+ wallpaper.connection.forEachDisplayConnector(
+ WallpaperConnection.DisplayConnector::disconnectLocked);
wallpaper.connection.mService = null;
wallpaper.connection.mDisplayConnector.clear();
wallpaper.connection = null;
@@ -2481,12 +2552,12 @@
}
}
- void clearWallpaperComponentLocked(WallpaperData wallpaper) {
+ private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
wallpaper.wallpaperComponent = null;
detachWallpaperLocked(wallpaper);
}
- void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
+ private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
}
@@ -2596,8 +2667,7 @@
if (DEBUG) {
Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
}
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- DEFAULT_DISPLAY);
+ final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
out.startTag(null, tag);
out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
out.attribute(null, "width", Integer.toString(wpdData.mWidth));
@@ -2755,10 +2825,10 @@
Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
}
}
+ initializeFallbackWallpaper();
}
boolean success = false;
- final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
- DEFAULT_DISPLAY);
+ final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
try {
stream = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
@@ -2845,8 +2915,19 @@
}
}
+ private void initializeFallbackWallpaper() {
+ if (mFallbackWallpaper == null) {
+ if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
+ mFallbackWallpaper = new WallpaperData(
+ UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
+ mFallbackWallpaper.allowBackup = false;
+ mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
+ bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+ }
+ }
+
private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
- final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+ final DisplayData size = getDisplayDataOrCreate(displayId);
if (displayId == DEFAULT_DISPLAY) {
// crop, if not previously specified
@@ -2869,7 +2950,7 @@
wallpaper.wallpaperId = makeWallpaperIdLocked();
}
- final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+ final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
if (!keepDimensionHints) {
wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
@@ -2963,7 +3044,7 @@
}
// Restore the named resource bitmap to both source + crop files
- boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
+ private boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
String resName = wallpaper.name.substring(4);
@@ -3048,8 +3129,9 @@
for (int i = 0; i < mWallpaperMap.size(); i++) {
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
- pw.print(": id="); pw.println(wallpaper.wallpaperId);
- forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(": id="); pw.println(wallpaper.wallpaperId);
+ pw.println(" Display state:");
+ forEachDisplayData(wpSize -> {
pw.print(" displayId=");
pw.println(wpSize.mDisplayId);
pw.print(" mWidth=");
@@ -3072,11 +3154,11 @@
pw.println(conn.mInfo.getComponent());
}
conn.forEachDisplayConnector(connector -> {
- pw.print(" mDisplayId=");
+ pw.print(" mDisplayId=");
pw.println(connector.mDisplayId);
- pw.print(" mToken=");
+ pw.print(" mToken=");
pw.println(connector.mToken);
- pw.print(" mEngine=");
+ pw.print(" mEngine=");
pw.println(connector.mEngine);
});
pw.print(" mService=");
@@ -3090,18 +3172,38 @@
WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
pw.print(": id="); pw.println(wallpaper.wallpaperId);
- forEachDisplayData(wallpaper, wpSize -> {
- pw.print(" displayId=");
- pw.println(wpSize.mDisplayId);
- pw.print(" mWidth="); pw.print(wpSize.mWidth);
- pw.print(" mHeight="); pw.println(wpSize.mHeight);
- pw.print(" mPadding="); pw.println(wpSize.mPadding);
- });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
}
-
+ pw.println("Fallback wallpaper state:");
+ pw.print(" User "); pw.print(mFallbackWallpaper.userId);
+ pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId);
+ pw.print(" mCropHint="); pw.println(mFallbackWallpaper.cropHint);
+ pw.print(" mName="); pw.println(mFallbackWallpaper.name);
+ pw.print(" mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup);
+ if (mFallbackWallpaper.connection != null) {
+ WallpaperConnection conn = mFallbackWallpaper.connection;
+ pw.print(" Fallback Wallpaper connection ");
+ pw.print(conn);
+ pw.println(":");
+ if (conn.mInfo != null) {
+ pw.print(" mInfo.component=");
+ pw.println(conn.mInfo.getComponent());
+ }
+ conn.forEachDisplayConnector(connector -> {
+ pw.print(" mDisplayId=");
+ pw.println(connector.mDisplayId);
+ pw.print(" mToken=");
+ pw.println(connector.mToken);
+ pw.print(" mEngine=");
+ pw.println(connector.mEngine);
+ });
+ pw.print(" mService=");
+ pw.println(conn.mService);
+ pw.print(" mLastDiedTime=");
+ pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis());
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8b8cadc..6d3c693 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -505,23 +505,6 @@
// TODO: Make it can take screenshot on external display
mScreenshotHelper = displayContent.isDefaultDisplay
? new ScreenshotHelper(mContext) : null;
- }
-
- void systemReady() {
- mSystemGestures.systemReady();
- }
-
- private int getDisplayId() {
- return mDisplayContent.getDisplayId();
- }
-
- void onDisplayRemoved() {
- mDisplayContent.unregisterPointerEventListener(mSystemGestures);
- }
-
- void configure(int width, int height, int shortSizeDp) {
- // Allow the navigation bar to move on non-square small devices (phones).
- mNavigationBarCanMove = width != height && shortSizeDp < 600;
if (mDisplayContent.isDefaultDisplay) {
mHasStatusBar = true;
@@ -541,6 +524,23 @@
}
}
+ void systemReady() {
+ mSystemGestures.systemReady();
+ }
+
+ private int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
+ void onDisplayRemoved() {
+ mDisplayContent.unregisterPointerEventListener(mSystemGestures);
+ }
+
+ void configure(int width, int height, int shortSizeDp) {
+ // Allow the navigation bar to move on non-square small devices (phones).
+ mNavigationBarCanMove = width != height && shortSizeDp < 600;
+ }
+
void updateConfigurationDependentBehaviors() {
mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode);
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f9c9d33..639ed02 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,6 +1,5 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -10,6 +9,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
+import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
@@ -204,37 +204,6 @@
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
- /** Callback to get pointer display id. */
- @Override
- public int getPointerDisplayId() {
- synchronized (mService.mGlobalLock) {
- // If desktop mode is not enabled, show on the default display.
- if (!mService.mForceDesktopModeOnExternalDisplays) {
- return DEFAULT_DISPLAY;
- }
-
- // Look for the topmost freeform display.
- int firstExternalDisplayId = DEFAULT_DISPLAY;
- for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
- // Heuristic solution here. Currently when "Freeform windows" developer option is
- // enabled we automatically put secondary displays in freeform mode and emulating
- // "desktop mode". It also makes sense to show the pointer on the same display.
- if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- return displayContent.getDisplayId();
- }
-
- if (firstExternalDisplayId == DEFAULT_DISPLAY
- && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
- firstExternalDisplayId = displayContent.getDisplayId();
- }
- }
-
- // Look for the topmost non-default display
- return firstExternalDisplayId;
- }
- }
-
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index bf83ca9..43d2dcf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,7 +107,6 @@
jmethodID getLongPressTimeout;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
- jmethodID getPointerDisplayId;
jmethodID getKeyboardLayoutOverlay;
jmethodID getDeviceAlias;
jmethodID getTouchCalibrationForInputDevice;
@@ -175,6 +174,15 @@
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
+static void updatePointerControllerFromViewport(
+ sp<PointerController> controller, const DisplayViewport* const viewport) {
+ if (controller != nullptr && viewport != nullptr) {
+ const int32_t width = viewport->logicalRight - viewport->logicalLeft;
+ const int32_t height = viewport->logicalBottom - viewport->logicalTop;
+ controller->setDisplayViewport(width, height, viewport->orientation);
+ }
+}
+
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -234,7 +242,6 @@
jfloatArray matrixArr);
virtual TouchAffineTransformation getTouchAffineTransformation(
const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
- virtual void updatePointerDisplay();
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -307,11 +314,10 @@
std::atomic<bool> mInteractive;
- void updateInactivityTimeoutLocked();
+ void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
- const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
- int32_t getPointerDisplayId();
+
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
static inline JNIEnv* jniEnv() {
@@ -385,10 +391,9 @@
return false;
}
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
- REQUIRES(mLock) {
- for (const DisplayViewport& v : mLocked.viewports) {
- if (v.displayId == displayId) {
+static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
+ for (const DisplayViewport& v : viewports) {
+ if (v.type == ViewportType::VIEWPORT_INTERNAL) {
return &v;
}
}
@@ -415,10 +420,20 @@
}
}
- { // acquire lock
+ const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
+ {
AutoMutex _l(mLock);
+ const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
+ // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
+ // if they are different.
+ const bool internalViewportChanged = (newInternalViewport != nullptr) &&
+ (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
+ if (internalViewportChanged) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ updatePointerControllerFromViewport(controller, newInternalViewport);
+ }
mLocked.viewports = viewports;
- } // release lock
+ }
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -541,43 +556,15 @@
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
- updateInactivityTimeoutLocked();
- }
+ const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
+ updatePointerControllerFromViewport(controller, internalViewport);
+
+ updateInactivityTimeoutLocked(controller);
+ }
return controller;
}
-int32_t NativeInputManager::getPointerDisplayId() {
- JNIEnv* env = jniEnv();
- jint pointerDisplayId = env->CallIntMethod(mServiceObj,
- gServiceClassInfo.getPointerDisplayId);
- if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
- pointerDisplayId = ADISPLAY_ID_DEFAULT;
- }
-
- return pointerDisplayId;
-}
-
-void NativeInputManager::updatePointerDisplay() {
- ATRACE_CALL();
-
- jint pointerDisplayId = getPointerDisplayId();
-
- AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- const DisplayViewport* viewport = findDisplayViewportLocked(pointerDisplayId);
- if (viewport == nullptr) {
- ALOGW("Can't find pointer display viewport, fallback to default display.");
- viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
- }
-
- if (viewport != nullptr) {
- controller->setDisplayViewport(*viewport);
- }
- }
-}
-
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
@@ -834,16 +821,16 @@
if (mLocked.systemUiVisibility != visibility) {
mLocked.systemUiVisibility = visibility;
- updateInactivityTimeoutLocked();
+
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != nullptr) {
+ updateInactivityTimeoutLocked(controller);
+ }
}
}
-void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller == nullptr) {
- return;
- }
-
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
+ REQUIRES(mLock) {
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1837,9 +1824,6 @@
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
- GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
- "getPointerDisplayId", "()I");
-
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index cee6fa9..35d29e7 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -39,7 +39,6 @@
import android.net.MacAddress;
import android.net.NetworkUtils;
import android.net.TrafficStats;
-import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -85,7 +84,7 @@
@NonNull
private final ServerHandler mHandler;
@NonNull
- private final InterfaceParams mIface;
+ private final String mIfName;
@NonNull
private final DhcpLeaseRepository mLeaseRepo;
@NonNull
@@ -161,20 +160,20 @@
}
}
- public DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+ public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log) {
- this(looper, iface, params, log, null);
+ this(looper, ifName, params, log, null);
}
@VisibleForTesting
- DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+ DhcpServer(@NonNull Looper looper, @NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log,
@Nullable Dependencies deps) {
if (deps == null) {
deps = new DependenciesImpl();
}
mHandler = new ServerHandler(looper);
- mIface = iface;
+ mIfName = ifName;
mServingParams = params;
mLog = log;
mDeps = deps;
@@ -444,7 +443,7 @@
private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) {
try {
- mDeps.addArpEntry(inetAddr, macAddr, mIface.name, mSocket);
+ mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket);
return true;
} catch (IOException e) {
mLog.e("Error adding client to ARP table", e);
@@ -526,7 +525,7 @@
// SO_BINDTODEVICE actually takes a string. This works because the first member
// of struct ifreq is a NULL-terminated interface name.
// TODO: add a setsockoptString()
- Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIface.name);
+ Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER);
NetworkUtils.protectFromVpn(mSocket);
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 823c0a1..493350d 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -138,9 +138,9 @@
return NetdService.getInstance();
}
- public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ public DhcpServer makeDhcpServer(Looper looper, String ifName,
DhcpServingParams params, SharedLog log) {
- return new DhcpServer(looper, iface, params, log);
+ return new DhcpServer(looper, ifName, params, log);
}
}
@@ -256,12 +256,6 @@
if (mUsingLegacyDhcp) {
return true;
}
-
- final InterfaceParams ifaceParams = mDeps.getInterfaceParams(mIfaceName);
- if (ifaceParams == null) {
- Log.e(TAG, "Failed to find interface params for DHCPv4");
- return false;
- }
final DhcpServingParams params;
try {
params = new DhcpServingParams.Builder()
@@ -277,7 +271,7 @@
return false;
}
- mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), ifaceParams, params,
+ mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
mLog.forSubComponent("DHCP"));
mDhcpServer.start();
return true;
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 6f10ed5..0c9c85a 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -27,6 +27,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
bmgrlib \
+ bu \
services.backup \
services.core \
services.net
diff --git a/services/robotests/src/com/android/commands/bu/AdbBackupTest.java b/services/robotests/src/com/android/commands/bu/AdbBackupTest.java
new file mode 100644
index 0000000..6869f56
--- /dev/null
+++ b/services/robotests/src/com/android/commands/bu/AdbBackupTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.commands.bu;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowParcelFileDescriptor;
+
+/** Unit tests for {@link com.android.commands.bu.Backup}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowParcelFileDescriptor.class})
+@Presubmit
+public class AdbBackupTest {
+ @Mock private IBackupManager mBackupManager;
+ private Backup mBackup;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mBackup = new Backup(mBackupManager);
+ }
+
+ @Test
+ public void testRun_whenUserNotSpecified_callsAdbBackupAsSystemUser() throws Exception {
+ mBackup.run(new String[] {"backup", "-all"});
+
+ verify(mBackupManager).isBackupServiceActive(UserHandle.USER_SYSTEM);
+ }
+
+ @Test
+ public void testRun_whenUserSpecified_callsBackupManagerAsSpecifiedUser() throws Exception {
+ mBackup.run(new String[] {"backup", "-user", "10", "-all"});
+
+ verify(mBackupManager).isBackupServiceActive(10);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 96ef0ce..83f66c5 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,11 +16,15 @@
package com.android.server.backup;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
import static com.android.server.backup.testing.TransportData.backupTransport;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import static org.testng.Assert.expectThrows;
@@ -37,8 +41,8 @@
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
-import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
import com.android.server.testing.shadows.ShadowBinder;
@@ -64,14 +68,14 @@
public class BackupManagerServiceTest {
private static final String TEST_PACKAGE = "package";
private static final String TEST_TRANSPORT = "transport";
-
- private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+ private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
private ShadowContextWrapper mShadowContext;
- @Mock private UserBackupManagerService mUserBackupManagerService;
- private BackupManagerService mBackupManagerService;
private Context mContext;
- @UserIdInt private int mUserId;
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+ @Mock private UserBackupManagerService mUserOneService;
+ @Mock private UserBackupManagerService mUserTwoService;
/** Initialize {@link BackupManagerService}. */
@Before
@@ -81,13 +85,11 @@
Application application = RuntimeEnvironment.application;
mContext = application;
mShadowContext = shadowOf(application);
- mUserId = NON_USER_SYSTEM;
- mBackupManagerService =
- new BackupManagerService(
- application,
- new Trampoline(application),
- BackupManagerServiceTestUtils.startBackupThread(null));
- mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
+
+ // TODO(b/120212806): Hardcoding system user for now since most methods in BMS don't yet
+ // take an user parameter (and instead hardcode the system user).
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
}
/**
@@ -100,8 +102,8 @@
}
/**
- * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
- * This is specifically to prevent overloading the logs in production.
+ * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+ * specifically to prevent overloading the logs in production.
*/
@Test
public void testMoreDebug_isFalse() throws Exception {
@@ -110,9 +112,73 @@
assertThat(moreDebug).isFalse();
}
- // TODO(b/118520567): Change the following tests to use the per-user instance of
- // UserBackupManagerService once it's implemented. Currently these tests only test the straight
- // forward redirection.
+ /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+ @Test
+ public void testConstructor_doesNotRegisterUsers() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0);
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullContext_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ /* context */ null,
+ new Trampoline(mContext),
+ startBackupThread(null)));
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullTrampoline_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ mContext, /* trampoline */ null, startBackupThread(null)));
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullBackupThread_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ mContext, new Trampoline(mContext), /* backupThread */ null));
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.startServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isNotNull();
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
+ }
+
+ // TODO(b/120212806): When BMS methods take in a user parameter, modify unknown user tests to
+ // check that that we don't call the method on another registered user. Currently these tests
+ // have no registered users since we hardcode the system user in BMS.
// ---------------------------------------------
// Backup agent tests
@@ -120,36 +186,88 @@
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDataChanged_callsDataChangedForUser() throws Exception {
- mBackupManagerService.dataChanged(TEST_PACKAGE);
+ public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
+ backupManagerService.dataChanged(TEST_PACKAGE);
+
+ verify(mUserOneService).dataChanged(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.dataChanged(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
+ public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
IBinder agentBinder = mock(IBinder.class);
- mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+ backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
- verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
+ verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ IBinder agentBinder = mock(IBinder.class);
+
+ backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+
+ verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
- mBackupManagerService.agentDisconnected(TEST_PACKAGE);
+ public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
+ backupManagerService.agentDisconnected(TEST_PACKAGE);
+
+ verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.agentDisconnected(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testOpComplete_callsOpCompleteForUser() throws Exception {
- mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+ public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
+ backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
}
// ---------------------------------------------
@@ -158,73 +276,168 @@
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
+ public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
String[] transports = {TEST_TRANSPORT};
- mBackupManagerService.initializeTransports(transports, /* observer */ null);
+ backupManagerService.initializeTransports(transports, /* observer */ null);
- verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
+ verify(mUserOneService).initializeTransports(transports, /* observer */ null);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ String[] transports = {TEST_TRANSPORT};
+
+ backupManagerService.initializeTransports(transports, /* observer */ null);
+
+ verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
- mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
- mBackupManagerService.getCurrentTransport();
+ public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getCurrentTransport();
+ backupManagerService.getCurrentTransport();
+
+ verify(mUserOneService).getCurrentTransport();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getCurrentTransport();
+
+ verify(mUserOneService, never()).getCurrentTransport();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
+ public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
throws Exception {
- mBackupManagerService.getCurrentTransportComponent();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getCurrentTransportComponent();
+ backupManagerService.getCurrentTransportComponent();
+
+ verify(mUserOneService).getCurrentTransportComponent();
}
- /** Test that the backup service routes methods correctly to the user that requests it. */
+ /** Test that the backup service does not route methods for non-registered users. */
@Test
- public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
- mBackupManagerService.listAllTransports();
-
- verify(mUserBackupManagerService).listAllTransports();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
+ public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
throws Exception {
- mBackupManagerService.listAllTransportComponents();
+ BackupManagerService backupManagerService = createService();
- verify(mUserBackupManagerService).listAllTransportComponents();
+ backupManagerService.getCurrentTransportComponent();
+
+ verify(mUserOneService, never()).getCurrentTransportComponent();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
- mBackupManagerService.getTransportWhitelist();
+ public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getTransportWhitelist();
+ backupManagerService.listAllTransports();
+
+ verify(mUserOneService).listAllTransports();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.listAllTransports();
+
+ verify(mUserOneService, never()).listAllTransports();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
+ public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.listAllTransportComponents();
+
+ verify(mUserOneService).listAllTransportComponents();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.listAllTransportComponents();
+
+ verify(mUserOneService, never()).listAllTransportComponents();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetTransportWhitelist_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getTransportWhitelist();
+
+ verify(mUserOneService).getTransportWhitelist();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetTransportWhitelist_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getTransportWhitelist();
+
+ verify(mUserOneService, never()).getTransportWhitelist();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
TransportData transport = backupTransport();
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
- mBackupManagerService.updateTransportAttributes(
+ backupManagerService.updateTransportAttributes(
transport.getTransportComponent(),
transport.transportName,
configurationIntent,
@@ -232,7 +445,34 @@
dataManagementIntent,
"dataManagementLabel");
- verify(mUserBackupManagerService)
+ verify(mUserOneService)
+ .updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ TransportData transport = backupTransport();
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+
+ backupManagerService.updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mUserOneService, never())
.updateTransportAttributes(
transport.getTransportComponent(),
transport.transportName,
@@ -244,136 +484,292 @@
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
- mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
+ public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
+ backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+ verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
+ public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
TransportData transport = backupTransport();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
- mBackupManagerService.selectBackupTransportAsync(
+ backupManagerService.selectBackupTransportAsync(
transport.getTransportComponent(), callback);
- verify(mUserBackupManagerService)
+ verify(mUserOneService)
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(
+ transport.getTransportComponent(), callback);
+
+ verify(mUserOneService, never())
.selectBackupTransportAsync(transport.getTransportComponent(), callback);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
- mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+ public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
+ backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
- mBackupManagerService.getDestinationString(TEST_TRANSPORT);
+ public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
+ backupManagerService.getDestinationString(TEST_TRANSPORT);
+
+ verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getDestinationString(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
- mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+ public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
+ backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
- mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+ public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
+ backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
}
// ---------------------------------------------
// Settings tests
// ---------------------------------------------
+
/**
- * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
*/
@Test
- public void setBackupEnabled_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
expectThrows(
SecurityException.class,
- () -> mBackupManagerService.setBackupEnabled(mUserId, true));
+ () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
}
/**
- * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not
- * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is
- * the same as the target user id.
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
*/
@Test
- public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
- ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
- mBackupManagerService.setBackupEnabled(mUserId, true);
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
- verify(mUserBackupManagerService).setBackupEnabled(true);
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.setBackupEnabled(mUserId, true);
-
- verify(mUserBackupManagerService).setBackupEnabled(true);
+ verify(mUserTwoService).setBackupEnabled(true);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
- mBackupManagerService.setAutoRestore(true);
+ public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- verify(mUserBackupManagerService).setAutoRestore(true);
+ backupManagerService.setBackupEnabled(mUserOneId, true);
+
+ verify(mUserOneService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+ verify(mUserOneService, never()).setBackupEnabled(true);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
- mBackupManagerService.setBackupProvisioned(true);
+ public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).setBackupProvisioned(true);
+ backupManagerService.setAutoRestore(true);
+
+ verify(mUserOneService).setAutoRestore(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setAutoRestore(true);
+
+ verify(mUserOneService, never()).setAutoRestore(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupProvisioned_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.setBackupProvisioned(true);
+
+ verify(mUserOneService).setBackupProvisioned(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupProvisioned_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setBackupProvisioned(true);
+
+ verify(mUserOneService, never()).setBackupProvisioned(true);
}
/**
- * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
*/
@Test
- public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testIsBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.isBackupEnabled(mUserId));
+ SecurityException.class, () -> backupManagerService.isBackupEnabled(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testIsBackupEnabled_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.isBackupEnabled(mUserTwoId);
+
+ verify(mUserTwoService).isBackupEnabled();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- mBackupManagerService.isBackupEnabled(mUserId);
+ backupManagerService.isBackupEnabled(mUserOneId);
- verify(mUserBackupManagerService).isBackupEnabled();
+ verify(mUserOneService).isBackupEnabled();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isBackupEnabled(mUserTwoId);
+
+ verify(mUserOneService, never()).isBackupEnabled();
}
// ---------------------------------------------
@@ -382,128 +778,290 @@
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
- mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+ public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
+ backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+ verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
+ public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
String[] packages = {TEST_PACKAGE};
- mBackupManagerService.filterAppsEligibleForBackup(packages);
+ backupManagerService.filterAppsEligibleForBackup(packages);
- verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
+ verify(mUserOneService).filterAppsEligibleForBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.filterAppsEligibleForBackup(packages);
+
+ verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
}
/**
- * Test verifying that {@link BackupManagerService#backupNow(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
*/
@Test
- public void testBackupNow_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.backupNow(mUserId));
+ expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testBackupNow_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserTwoService).backupNow();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testBackupNow_callsBackupNowForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- mBackupManagerService.backupNow(mUserId);
+ backupManagerService.backupNow(mUserOneId);
- verify(mUserBackupManagerService).backupNow();
+ verify(mUserOneService).backupNow();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserOneService, never()).backupNow();
}
/**
- * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver,
- * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission.
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
*/
@Test
- public void testRequestBackup_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
IBackupObserver observer = mock(IBackupObserver.class);
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
expectThrows(
SecurityException.class,
- () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0));
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRequestBackup_callsRequestBackupForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
- mBackupManagerService.requestBackup(mUserId, packages, observer, monitor,
- /* flags */ 0);
-
- verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ () ->
+ backupManagerService.requestBackup(
+ mUserTwoId, packages, observer, monitor, 0));
}
/**
- * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
*/
@Test
- public void testCancelBackups_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.cancelBackups(mUserId));
- }
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.cancelBackups(mUserId);
-
- verify(mUserBackupManagerService).cancelBackups();
+ verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
+ public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserTwoService).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserOneId);
+
+ verify(mUserOneService).cancelBackups();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserOneService, never()).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
FullBackupJob job = new FullBackupJob();
- mBackupManagerService.beginFullBackup(job);
+ backupManagerService.beginFullBackup(job);
- verify(mUserBackupManagerService).beginFullBackup(job);
+ verify(mUserOneService).beginFullBackup(job);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ FullBackupJob job = new FullBackupJob();
+
+ backupManagerService.beginFullBackup(job);
+
+ verify(mUserOneService, never()).beginFullBackup(job);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
- mBackupManagerService.endFullBackup();
+ public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).endFullBackup();
+ backupManagerService.endFullBackup();
+
+ verify(mUserOneService).endFullBackup();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.endFullBackup();
+
+ verify(mUserOneService, never()).endFullBackup();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
+ public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
String[] packages = {TEST_PACKAGE};
- mBackupManagerService.fullTransportBackup(packages);
+ backupManagerService.fullTransportBackup(packages);
- verify(mUserBackupManagerService).fullTransportBackup(packages);
+ verify(mUserOneService).fullTransportBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.fullTransportBackup(packages);
+
+ verify(mUserOneService, never()).fullTransportBackup(packages);
}
// ---------------------------------------------
@@ -512,27 +1070,66 @@
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
- mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
- mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
+ public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
throws Exception {
- mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
+ backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+
+ verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
}
// ---------------------------------------------
@@ -541,30 +1138,87 @@
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
- mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
+ public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService).setBackupPassword("currentPassword", "newPassword");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword");
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
- mBackupManagerService.hasBackupPassword();
+ public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
- verify(mUserBackupManagerService).hasBackupPassword();
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService).hasBackupPassword();
}
- /** Test that the backup service routes methods correctly to the user that requests it. */
+ /** Test that the backup service does not route methods for non-registered users. */
@Test
- public void testAdbBackup_callsAdbBackupForUser() throws Exception {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- ParcelFileDescriptor parcelFileDescriptor =
- ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
- String[] packages = {TEST_PACKAGE};
+ public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
- mBackupManagerService.adbBackup(
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService, never()).hasBackupPassword();
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ /* parcelFileDescriptor*/ null,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
parcelFileDescriptor,
/* includeApks */ true,
/* includeObbs */ true,
@@ -574,9 +1228,9 @@
/* includeSystem */ true,
/* doCompress */ true,
/* doKeyValue */ true,
- packages);
+ ADB_TEST_PACKAGES);
- verify(mUserBackupManagerService)
+ verify(mUserTwoService)
.adbBackup(
parcelFileDescriptor,
/* includeApks */ true,
@@ -587,32 +1241,167 @@
/* includeSystem */ true,
/* doCompress */ true,
/* doKeyValue */ true,
- packages);
+ ADB_TEST_PACKAGES);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- ParcelFileDescriptor parcelFileDescriptor =
- ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+ public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- mBackupManagerService.adbRestore(parcelFileDescriptor);
+ backupManagerService.adbBackup(
+ mUserOneId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
- verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
+ verify(mUserOneService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserOneService, never())
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserTwoService).adbRestore(parcelFileDescriptor);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
+ public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor);
+
+ verify(mUserOneService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
- mBackupManagerService.acknowledgeAdbBackupOrRestore(
+ backupManagerService.acknowledgeAdbBackupOrRestore(
/* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
- verify(mUserBackupManagerService)
+ verify(mUserOneService)
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+ backupManagerService.acknowledgeAdbBackupOrRestore(
+ /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+ verify(mUserOneService, never())
.acknowledgeAdbBackupOrRestore(
/* token */ 0,
/* allow */ true,
@@ -627,15 +1416,65 @@
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDump_callsDumpForUser() throws Exception {
+ public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
File testFile = new File(mContext.getFilesDir(), "test");
testFile.createNewFile();
FileDescriptor fileDescriptor = new FileDescriptor();
PrintWriter printWriter = new PrintWriter(testFile);
String[] args = {"1", "2"};
- mBackupManagerService.dump(fileDescriptor, printWriter, args);
+ backupManagerService.dump(fileDescriptor, printWriter, args);
- verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
+ verify(mUserOneService).dump(fileDescriptor, printWriter, args);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
+
+ backupManagerService.dump(fileDescriptor, printWriter, args);
+
+ verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
+ }
+
+ private BackupManagerService createService() {
+ return new BackupManagerService(
+ mContext, new Trampoline(mContext), startBackupThread(null));
+ }
+
+ private BackupManagerService createServiceAndRegisterUser(
+ int userId, UserBackupManagerService userBackupManagerService) {
+ BackupManagerService backupManagerService = createService();
+ backupManagerService.startServiceForUser(userId, userBackupManagerService);
+ return backupManagerService;
+ }
+
+ /**
+ * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
+ * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
+ * permission.
+ */
+ private void setCallerAndGrantInteractUserPermission(
+ @UserIdInt int userId, boolean shouldGrantPermission) {
+ ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
+ if (shouldGrantPermission) {
+ mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+ } else {
+ mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
+ }
+ }
+
+ private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 3979a8e..148faad 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -25,7 +25,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -42,7 +41,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -66,7 +64,6 @@
import android.util.Log;
import android.util.SparseArray;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -96,6 +93,8 @@
@Mock
private ContentResolver mMockResolver;
@Mock
+ private Context mMockContext;
+ @Mock
private IActivityManager mIActivityManager;
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -221,17 +220,16 @@
.thenReturn(STANDBY_BUCKET_ACTIVE);
doReturn(Looper.getMainLooper()).when(Looper::myLooper);
- final Context context = spy(InstrumentationRegistry.getTargetContext());
- when(context.getContentResolver()).thenReturn(mMockResolver);
- doNothing().when(mMockResolver).registerContentObserver(any(), anyBoolean(), any());
+ when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
doReturn("min_futurity=0").when(() ->
Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
- mInjector = new Injector(context);
- mService = new AlarmManagerService(context, mInjector);
+ mInjector = new Injector(mMockContext);
+ mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
doNothing().when(mService).publishBinderService(any(), any());
mService.onStart();
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ spyOn(mService.mHandler);
assertEquals(0, mService.mConstants.MIN_FUTURITY);
assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
@@ -273,7 +271,7 @@
final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
- verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class),
+ verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
verify(mWakeLock).acquire();
onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
@@ -423,11 +421,23 @@
assertNotNull(restrictedAlarms.get(TEST_CALLING_UID));
listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID);
- verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), any(),
+ verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), any(),
any(Handler.class), isNull(), any());
assertNull(restrictedAlarms.get(TEST_CALLING_UID));
}
+ @Test
+ public void sendsTimeTickOnInteractive() {
+ final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ // Stubbing so the handler doesn't actually run the runnable.
+ doReturn(true).when(mService.mHandler).post(runnableCaptor.capture());
+ // change interactive state: false -> true
+ mService.interactiveStateChangedLocked(false);
+ mService.interactiveStateChangedLocked(true);
+ runnableCaptor.getValue().run();
+ verify(mMockContext).sendBroadcastAsUser(mService.mTimeTickIntent, UserHandle.ALL);
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 04a8408..cff0521 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -203,6 +203,8 @@
.strictness(Strictness.LENIENT)
.mockStatic(LocalServices.class)
.startMocking();
+ spyOn(getContext());
+ doReturn(null).when(getContext()).registerReceiver(any(), any());
doReturn(mock(ActivityManagerInternal.class))
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mock(ActivityTaskManagerInternal.class))
diff --git a/services/tests/servicestests/res/values/strings.xml b/services/tests/servicestests/res/values/strings.xml
index 57da0af..50ccd1f 100644
--- a/services/tests/servicestests/res/values/strings.xml
+++ b/services/tests/servicestests/res/values/strings.xml
@@ -32,4 +32,6 @@
<string name="config_batterySaverDeviceSpecificConfig_1"></string>
<string name="config_batterySaverDeviceSpecificConfig_2">cpufreq-n=1:123/2:456</string>
<string name="config_batterySaverDeviceSpecificConfig_3">cpufreq-n=2:222,cpufreq-i=3:333/4:444</string>
+ <string name="module_1_name" translatable="false">module_1_name</string>
+ <string name="module_2_name" translatable="false">module_2_name</string>
</resources>
diff --git a/services/tests/servicestests/res/xml/unparseable_metadata1.xml b/services/tests/servicestests/res/xml/unparseable_metadata1.xml
new file mode 100644
index 0000000..73967f1
--- /dev/null
+++ b/services/tests/servicestests/res/xml/unparseable_metadata1.xml
@@ -0,0 +1,4 @@
+<not-module-metadata>
+ <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"/>
+ <module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</not-module-metadata>
diff --git a/services/tests/servicestests/res/xml/unparseable_metadata2.xml b/services/tests/servicestests/res/xml/unparseable_metadata2.xml
new file mode 100644
index 0000000..bb5a1b2
--- /dev/null
+++ b/services/tests/servicestests/res/xml/unparseable_metadata2.xml
@@ -0,0 +1,4 @@
+<module-metadata>
+ <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"/>
+ <not-module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</module-metadata>
diff --git a/services/tests/servicestests/res/xml/well_formed_metadata.xml b/services/tests/servicestests/res/xml/well_formed_metadata.xml
new file mode 100644
index 0000000..17cc369
--- /dev/null
+++ b/services/tests/servicestests/res/xml/well_formed_metadata.xml
@@ -0,0 +1,4 @@
+<module-metadata>
+ <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"/>
+ <module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</module-metadata>
diff --git a/services/tests/servicestests/res/xml/well_formed_metadata2.xml b/services/tests/servicestests/res/xml/well_formed_metadata2.xml
new file mode 100644
index 0000000..47279e6
--- /dev/null
+++ b/services/tests/servicestests/res/xml/well_formed_metadata2.xml
@@ -0,0 +1,5 @@
+<module-metadata>
+ <module name="@string/module_1_name" packageName="com.android.module1" isHidden="false"
+ attribute1="attribute1" attribute2="attribute2" />
+ <module name="@string/module_2_name" packageName="com.android.module2" isHidden="true"/>
+</module-metadata>
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 89c7b71..93cac08 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -20,6 +20,7 @@
import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
+import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
@@ -31,6 +32,7 @@
import org.junit.Test;
+import java.io.ByteArrayOutputStream;
import java.util.Collections;
/**
@@ -232,4 +234,41 @@
assertEquals(0, parseVmHWMFromProcfs(null));
}
+
+ @Test
+ public void testParseCmdlineFromProcfs_invalidValue() {
+ byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
+
+ assertEquals("", parseCmdlineFromProcfs(bytesToString(nothing)));
+ }
+
+ @Test
+ public void testParseCmdlineFromProcfs_correctValue_noNullBytes() {
+ assertEquals("com.google.app", parseCmdlineFromProcfs("com.google.app"));
+ }
+
+ @Test
+ public void testParseCmdlineFromProcfs_correctValue_withNullBytes() {
+ byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0
+
+ assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
+
+ // test\0\0test
+ byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74};
+
+ assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
+ }
+
+ @Test
+ public void testParseCmdlineFromProcfs_emptyContents() {
+ assertEquals("", parseCmdlineFromProcfs(""));
+
+ assertEquals("", parseCmdlineFromProcfs(null));
+ }
+
+ private static String bytesToString(byte[] bytes) {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ output.write(bytes, 0, bytes.length);
+ return output.toString();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 751ed9b..ff31435 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -132,19 +132,21 @@
}
@Test
- public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
+ public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.startServiceForUser(10);
+ mTrampoline.unlockUser(10);
verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
}
@Test
- public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
+ public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.startServiceForUser(10);
+ mTrampoline.unlockUser(10);
verify(mBackupManagerServiceMock).startServiceForUser(10);
}
@@ -487,8 +489,8 @@
@Test
public void adbBackup_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.adbBackup(mParcelFileDescriptorMock, true, true, true, true, true, true, true,
- true,
+ mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
+ true, true, true, true, true, true,
PACKAGE_NAMES);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@@ -496,12 +498,11 @@
@Test
public void adbBackup_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.adbBackup(mParcelFileDescriptorMock, true, true, true, true, true, true, true,
- true,
+ mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
+ true, true, true, true, true, true,
PACKAGE_NAMES);
- verify(mBackupManagerServiceMock).adbBackup(mParcelFileDescriptorMock, true, true, true,
- true,
- true, true, true, true, PACKAGE_NAMES);
+ verify(mBackupManagerServiceMock).adbBackup(mUserId, mParcelFileDescriptorMock, true,
+ true, true, true, true, true, true, true, PACKAGE_NAMES);
}
@Test
@@ -519,15 +520,15 @@
@Test
public void adbRestore_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.adbRestore(mParcelFileDescriptorMock);
+ mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
public void adbRestore_forwarded() throws RemoteException {
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.adbRestore(mParcelFileDescriptorMock);
- verify(mBackupManagerServiceMock).adbRestore(mParcelFileDescriptorMock);
+ mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
+ verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock);
}
@Test
@@ -990,6 +991,11 @@
return sBackupManagerServiceMock;
}
+ @Override
+ protected void postToHandler(Runnable runnable) {
+ runnable.run();
+ }
+
int getCreateServiceCallsCount() {
return mCreateServiceCallsCount;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
new file mode 100644
index 0000000..bd3d9ab
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.content.Context;
+import android.content.pm.ModuleInfo;
+import android.test.InstrumentationTestCase;
+
+import com.android.frameworks.servicestests.R;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ModuleInfoProviderTest extends InstrumentationTestCase {
+ public void testSuccessfulParse() {
+ ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
+
+ List<ModuleInfo> mi = provider.getInstalledModules(0);
+ assertEquals(2, mi.size());
+
+ Collections.sort(mi, (ModuleInfo m1, ModuleInfo m2) ->
+ m1.getPackageName().compareTo(m1.getPackageName()));
+ assertEquals("com.android.module1", mi.get(0).getPackageName());
+ assertEquals("com.android.module2", mi.get(1).getPackageName());
+
+ ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0);
+ assertEquals("com.android.module1", mi1.getPackageName());
+ assertEquals("module_1_name", mi1.getName());
+ assertEquals(false, mi1.isHidden());
+
+ ModuleInfo mi2 = provider.getModuleInfo("com.android.module2", 0);
+ assertEquals("com.android.module2", mi2.getPackageName());
+ assertEquals("module_2_name", mi2.getName());
+ assertEquals(true, mi2.isHidden());
+ }
+
+ public void testParseFailure_incorrectTopLevelElement() {
+ ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata1);
+ assertEquals(0, provider.getInstalledModules(0).size());
+ }
+
+ public void testParseFailure_incorrectModuleElement() {
+ ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata2);
+ assertEquals(0, provider.getInstalledModules(0).size());
+ }
+
+ public void testParse_unknownAttributesIgnored() {
+ ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
+
+ List<ModuleInfo> mi = provider.getInstalledModules(0);
+ assertEquals(2, mi.size());
+
+ ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0);
+ assertEquals("com.android.module1", mi1.getPackageName());
+ assertEquals("module_1_name", mi1.getName());
+ assertEquals(false, mi1.isHidden());
+ }
+
+ /**
+ * Constructs an {@code ModuleInfoProvider} using the test package resources.
+ */
+ private ModuleInfoProvider getProvider(int resourceId) {
+ final Context ctx = getInstrumentation().getContext();
+ return new ModuleInfoProvider(ctx.getResources().getXml(resourceId), ctx.getResources());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 7755e94..5df4509 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -17,6 +17,7 @@
package com.android.server.pm.dex;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.MAX_SECONDARY_FILES_PER_OWNER;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
import static org.junit.Assert.assertEquals;
@@ -31,24 +32,28 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import dalvik.system.VMRuntime;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import dalvik.system.VMRuntime;
-
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageDexUsageTests {
+ private static final String ISA = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+
private PackageDexUsage mPackageDexUsage;
private TestData mFooBaseUser0;
@@ -71,25 +76,23 @@
String fooCodeDir = "/data/app/com.google.foo/";
String fooDataDir = "/data/user/0/com.google.foo/";
- String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
-
mFooBaseUser0 = new TestData(fooPackageName,
- fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName);
+ fooCodeDir + "base.apk", 0, ISA, false, true, fooPackageName);
mFooSplit1User0 = new TestData(fooPackageName,
- fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName);
+ fooCodeDir + "split-1.apk", 0, ISA, false, true, fooPackageName);
mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
- fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com");
+ fooCodeDir + "split-2.apk", 0, ISA, true, true, "used.by.other.com");
mFooSecondary1User0 = new TestData(fooPackageName,
- fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName);
+ fooDataDir + "sec-1.dex", 0, ISA, false, false, fooPackageName);
mFooSecondary1User1 = new TestData(fooPackageName,
- fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName);
+ fooDataDir + "sec-1.dex", 1, ISA, false, false, fooPackageName);
mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
- fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com");
+ fooDataDir + "sec-2.dex", 0, ISA, true, false, "used.by.other.com");
mInvalidIsa = new TestData(fooPackageName,
fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
@@ -100,11 +103,11 @@
String barDataDir1 = "/data/user/1/com.google.bar/";
mBarBaseUser0 = new TestData(barPackageName,
- barCodeDir + "base.apk", 0, isa, false, true, barPackageName);
+ barCodeDir + "base.apk", 0, ISA, false, true, barPackageName);
mBarSecondary1User0 = new TestData(barPackageName,
- barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName);
+ barDataDir + "sec-1.dex", 0, ISA, false, false, barPackageName);
mBarSecondary2User1 = new TestData(barPackageName,
- barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName);
+ barDataDir1 + "sec-2.dex", 1, ISA, false, false, barPackageName);
}
@Test
@@ -183,6 +186,25 @@
}
@Test
+ public void testRecordTooManySecondaries() {
+ int tooManyFiles = MAX_SECONDARY_FILES_PER_OWNER + 1;
+ List<TestData> expectedSecondaries = new ArrayList<>();
+ for (int i = 1; i <= tooManyFiles; i++) {
+ String fooPackageName = "com.google.foo";
+ TestData testData = new TestData(fooPackageName,
+ "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, false,
+ fooPackageName);
+ if (i < tooManyFiles) {
+ assertTrue("Adding " + testData.mDexFile, record(testData));
+ expectedSecondaries.add(testData);
+ } else {
+ assertFalse("Adding " + testData.mDexFile, record(testData));
+ }
+ assertPackageDexUsage(mPackageDexUsage, null, null, expectedSecondaries);
+ }
+ }
+
+ @Test
public void testMultiplePackages() {
assertTrue(record(mFooBaseUser0));
assertTrue(record(mFooSecondary1User0));
@@ -540,7 +562,14 @@
private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
TestData primary, TestData... secondaries) {
- String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
+ assertPackageDexUsage(packageDexUsage, users, primary, Arrays.asList(secondaries));
+ }
+
+ private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
+ TestData primary, List<TestData> secondaries) {
+ String packageName = primary == null
+ ? secondaries.get(0).mPackageName
+ : primary.mPackageName;
boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps;
PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
@@ -554,7 +583,7 @@
}
Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
- assertEquals(secondaries.length, dexUseInfoMap.size());
+ assertEquals(secondaries.size(), dexUseInfoMap.size());
// Check dex use info
for (TestData testData : secondaries) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 9bd3f26..68d3e4c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -17,20 +17,33 @@
package com.android.server.notification;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
import android.app.NotificationManager.Policy;
+import android.content.ComponentName;
import android.net.Uri;
+import android.provider.Settings;
+import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenPolicy;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -138,6 +151,54 @@
assertEquals(event, eventParsed);
}
+ @Test
+ public void testRuleXml() throws Exception {
+ String tag = "tag";
+
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = new ComponentName("a", "a");
+ rule.component = new ComponentName("a", "b");
+ rule.conditionId = new Uri.Builder().scheme("hello").build();
+ rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE);
+ rule.enabled = true;
+ rule.creationTime = 123;
+ rule.id = "id";
+ rule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
+ rule.modified = true;
+ rule.name = "name";
+ rule.snoozing = true;
+
+ XmlSerializer out = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ out.setOutput(new BufferedOutputStream(baos), "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, tag);
+ ZenModeConfig.writeRuleXml(rule, out);
+ out.endTag(null, tag);
+ out.endDocument();
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
+ // read from backing component
+ assertEquals("a", fromXml.pkg);
+ // always resets on reboot
+ assertFalse(fromXml.snoozing);
+ //should all match original
+ assertEquals(rule.component, fromXml.component);
+ assertEquals(rule.configurationActivity, fromXml.configurationActivity);
+ assertNull(fromXml.enabler);
+ assertEquals(rule.condition, fromXml.condition);
+ assertEquals(rule.enabled, fromXml.enabled);
+ assertEquals(rule.creationTime, fromXml.creationTime);
+ assertEquals(rule.modified, fromXml.modified);
+ assertEquals(rule.conditionId, fromXml.conditionId);
+ assertEquals(rule.name, fromXml.name);
+ assertEquals(rule.zenMode, fromXml.zenMode);
+ }
+
private ZenModeConfig getMutedNotificationsConfig() {
ZenModeConfig config = new ZenModeConfig();
// Allow alarms, media, and system
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 6c7ede3..dc3287e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -633,8 +633,8 @@
mZenModeHelperSpy.mConfig.manualRule.zenMode =
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
+ mZenModeHelperSpy.mConfig.manualRule.pkg = "a";
mZenModeHelperSpy.mConfig.manualRule.enabled = true;
- mZenModeHelperSpy.mConfig.manualRule.snoozing = true;
ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
@@ -645,7 +645,8 @@
parser.nextTag();
mZenModeHelperSpy.readXml(parser, false);
- assertEquals(expected, mZenModeHelperSpy.mConfig);
+ assertEquals("Config mismatch: current vs expected: "
+ + mZenModeHelperSpy.mConfig.diff(expected), expected, mZenModeHelperSpy.mConfig);
}
@Test
@@ -662,7 +663,9 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
- customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+ customRule.configurationActivity
+ = new ComponentName("android", "ScheduleConditionProvider");
+ customRule.pkg = customRule.configurationActivity.getPackageName();
automaticRules.put("customRule", customRule);
mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
@@ -674,7 +677,8 @@
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
mZenModeHelperSpy.readXml(parser, true);
- assertEquals(original, mZenModeHelperSpy.mConfig);
+ assertEquals("Config mismatch: current vs original: "
+ + mZenModeHelperSpy.mConfig.diff(original), original, mZenModeHelperSpy.mConfig);
assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode());
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index ec475bf..eddf8f9 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -98,14 +98,14 @@
stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_VISIBLE_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mLastTimeVisible", e);
+ Log.i(TAG, "Failed to parse mLastTimeVisible");
}
try {
stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+ Log.i(TAG, "Failed to parse mLastTimeForegroundServiceUsed");
}
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
@@ -113,14 +113,14 @@
try {
stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mTotalTimeVisible", e);
+ Log.i(TAG, "Failed to parse mTotalTimeVisible");
}
try {
stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
TOTAL_TIME_SERVICE_USED_ATTR);
} catch (IOException e) {
- Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+ Log.i(TAG, "Failed to parse mTotalTimeForegroundServiceUsed");
}
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 99ad1f4..bbf3d45 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -356,7 +356,12 @@
}
// No voice interactor, we'll just set up a simple recognizer.
- curRecognizer = findAvailRecognizer(null, userHandle);
+ initSimpleRecognizer(curInteractorInfo, userHandle);
+ }
+
+ public void initSimpleRecognizer(VoiceInteractionServiceInfo curInteractorInfo,
+ int userHandle) {
+ ComponentName curRecognizer = findAvailRecognizer(null, userHandle);
if (curRecognizer != null) {
if (curInteractorInfo == null) {
setCurInteractor(null, userHandle);
@@ -1236,34 +1241,46 @@
int userHandle = UserHandle.getUserId(uid);
ComponentName curInteractor = getCurInteractor(userHandle);
ComponentName curRecognizer = getCurRecognizer(userHandle);
- boolean hit = false;
+ boolean hitInt = false;
+ boolean hitRec = false;
for (String pkg : packages) {
if (curInteractor != null && pkg.equals(curInteractor.getPackageName())) {
- hit = true;
+ hitInt = true;
break;
} else if (curRecognizer != null
&& pkg.equals(curRecognizer.getPackageName())) {
- hit = true;
+ hitRec = true;
break;
}
}
- if (hit && doit) {
- // The user is force stopping our current interactor/recognizer.
+ if (hitInt && doit) {
+ // The user is force stopping our current interactor.
// Clear the current settings and restore default state.
synchronized (VoiceInteractionManagerServiceStub.this) {
+ Slog.i(TAG, "Force stopping current voice interactor: "
+ + getCurInteractor(userHandle));
unloadAllKeyphraseModels();
if (mImpl != null) {
mImpl.shutdownLocked();
setImplLocked(null);
}
+
setCurInteractor(null, userHandle);
setCurRecognizer(null, userHandle);
resetCurAssistant(userHandle);
initForUser(userHandle);
switchImplementationIfNeededLocked(true);
}
+ } else if (hitRec && doit) {
+ // We are just force-stopping the current recognizer, which is not
+ // also the current interactor.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ Slog.i(TAG, "Force stopping current voice recognizer: "
+ + getCurRecognizer(userHandle));
+ initSimpleRecognizer(null, userHandle);
+ }
}
- return hit;
+ return hitInt || hitRec;
}
@Override
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index de40e0d..91cec554 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -24,6 +24,9 @@
"libdexfile",
"slicer",
],
+ static_libs: [
+ "libtinyxml2",
+ ],
}
cc_library_host_static {
@@ -32,7 +35,9 @@
srcs: [
"dex_builder.cc",
"java_lang_builder.cc",
+ "tinyxml_layout_parser.cc",
"util.cc",
+ "layout_validation.cc",
],
}
@@ -43,7 +48,6 @@
"main.cc",
],
static_libs: [
- "libtinyxml2",
"libgflags",
"libviewcompiler",
],
@@ -54,6 +58,7 @@
defaults: ["viewcompiler_defaults"],
srcs: [
"dex_builder_test.cc",
+ "layout_validation_test.cc",
"util_test.cc",
],
static_libs: [
diff --git a/startop/view_compiler/layout_validation.cc b/startop/view_compiler/layout_validation.cc
new file mode 100644
index 0000000..8c77377
--- /dev/null
+++ b/startop/view_compiler/layout_validation.cc
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include "layout_validation.h"
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
+ if (0 == name.compare(u"merge")) {
+ message_ = "Merge tags are not supported";
+ can_compile_ = false;
+ }
+ if (0 == name.compare(u"include")) {
+ message_ = "Include tags are not supported";
+ can_compile_ = false;
+ }
+ if (0 == name.compare(u"view")) {
+ message_ = "View tags are not supported";
+ can_compile_ = false;
+ }
+ if (0 == name.compare(u"fragment")) {
+ message_ = "Fragment tags are not supported";
+ can_compile_ = false;
+ }
+}
+
+} // namespace startop
\ No newline at end of file
diff --git a/startop/view_compiler/layout_validation.h b/startop/view_compiler/layout_validation.h
new file mode 100644
index 0000000..bed34bb
--- /dev/null
+++ b/startop/view_compiler/layout_validation.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef LAYOUT_VALIDATION_H_
+#define LAYOUT_VALIDATION_H_
+
+#include "dex_builder.h"
+
+#include <string>
+
+namespace startop {
+
+// This visitor determines whether a layout can be compiled. Since we do not currently support all
+// features, such as includes and merges, we need to pre-validate the layout before we start
+// compiling.
+class LayoutValidationVisitor {
+ public:
+ void VisitStartDocument() const {}
+ void VisitEndDocument() const {}
+ void VisitStartTag(const std::u16string& name);
+ void VisitEndTag() const {}
+
+ const std::string& message() const { return message_; }
+ bool can_compile() const { return can_compile_; }
+
+ private:
+ std::string message_{"Okay"};
+ bool can_compile_{true};
+};
+
+} // namespace startop
+
+#endif // LAYOUT_VALIDATION_H_
diff --git a/startop/view_compiler/layout_validation_test.cc b/startop/view_compiler/layout_validation_test.cc
new file mode 100644
index 0000000..b74cdae
--- /dev/null
+++ b/startop/view_compiler/layout_validation_test.cc
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+#include "tinyxml_layout_parser.h"
+
+#include "gtest/gtest.h"
+
+using startop::CanCompileLayout;
+using std::string;
+
+namespace {
+void ValidateXmlText(const string& xml, bool expected) {
+ tinyxml2::XMLDocument doc;
+ doc.Parse(xml.c_str());
+ EXPECT_EQ(CanCompileLayout(doc), expected);
+}
+} // namespace
+
+TEST(LayoutValidationTest, SingleButtonLayout) {
+ const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="Hello, World!">
+
+</Button>)";
+ ValidateXmlText(xml, /*expected=*/true);
+}
+
+TEST(LayoutValidationTest, SmallConstraintLayout) {
+ const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/button6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:layout_marginBottom="16dp"
+ android:text="Button"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <Button
+ android:id="@+id/button7"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="16dp"
+ android:text="Button2"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/button6" />
+
+ <Button
+ android:id="@+id/button8"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="16dp"
+ android:text="Button1"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/button7" />
+</android.support.constraint.ConstraintLayout>)";
+ ValidateXmlText(xml, /*expected=*/true);
+}
+
+TEST(LayoutValidationTest, MergeNode) {
+ const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <TextView
+ android:id="@+id/textView3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView" />
+
+ <Button
+ android:id="@+id/button9"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+</merge>)";
+ ValidateXmlText(xml, /*expected=*/false);
+}
+
+TEST(LayoutValidationTest, IncludeLayout) {
+ const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include
+ layout="@layout/single_button_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+</android.support.constraint.ConstraintLayout>)";
+ ValidateXmlText(xml, /*expected=*/false);
+}
+
+TEST(LayoutValidationTest, ViewNode) {
+ const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <view
+ class="android.support.design.button.MaterialButton"
+ id="@+id/view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+</android.support.constraint.ConstraintLayout>)";
+ ValidateXmlText(xml, /*expected=*/false);
+}
+
+TEST(LayoutValidationTest, FragmentNode) {
+ // This test case is from https://developer.android.com/guide/components/fragments
+ const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment android:name="com.example.news.ArticleListFragment"
+ android:id="@+id/list"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+ <fragment android:name="com.example.news.ArticleReaderFragment"
+ android:id="@+id/viewer"
+ android:layout_weight="2"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+</LinearLayout>)";
+ ValidateXmlText(xml, /*expected=*/false);
+}
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 7d791c2..9351dc3 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -18,6 +18,7 @@
#include "dex_builder.h"
#include "java_lang_builder.h"
+#include "tinyxml_layout_parser.h"
#include "util.h"
#include "tinyxml2.h"
@@ -100,6 +101,12 @@
XMLDocument xml;
xml.LoadFile(filename);
+ string message{};
+ if (!startop::CanCompileLayout(xml, &message)) {
+ LOG(ERROR) << "Layout not supported: " << message;
+ return 1;
+ }
+
std::ofstream outfile;
if (FLAGS_out != kStdoutFilename) {
outfile.open(FLAGS_out);
diff --git a/startop/view_compiler/tinyxml_layout_parser.cc b/startop/view_compiler/tinyxml_layout_parser.cc
new file mode 100644
index 0000000..1b3a81f
--- /dev/null
+++ b/startop/view_compiler/tinyxml_layout_parser.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "tinyxml_layout_parser.h"
+
+#include "layout_validation.h"
+
+namespace startop {
+
+bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message) {
+ LayoutValidationVisitor validator;
+ TinyXmlVisitorAdapter adapter{&validator};
+ xml.Accept(&adapter);
+
+ if (message != nullptr) {
+ *message = validator.message();
+ }
+
+ return validator.can_compile();
+}
+
+} // namespace startop
diff --git a/startop/view_compiler/tinyxml_layout_parser.h b/startop/view_compiler/tinyxml_layout_parser.h
new file mode 100644
index 0000000..8f714a2
--- /dev/null
+++ b/startop/view_compiler/tinyxml_layout_parser.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+#ifndef TINYXML_LAYOUT_PARSER_H_
+#define TINYXML_LAYOUT_PARSER_H_
+
+#include "tinyxml2.h"
+
+#include <codecvt>
+#include <locale>
+#include <string>
+
+namespace startop {
+
+template <typename Visitor>
+class TinyXmlVisitorAdapter : public tinyxml2::XMLVisitor {
+ public:
+ explicit TinyXmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
+
+ bool VisitEnter(const tinyxml2::XMLDocument& /*doc*/) override {
+ visitor_->VisitStartDocument();
+ return true;
+ }
+
+ bool VisitExit(const tinyxml2::XMLDocument& /*doc*/) override {
+ visitor_->VisitEndDocument();
+ return true;
+ }
+
+ bool VisitEnter(const tinyxml2::XMLElement& element,
+ const tinyxml2::XMLAttribute* /*firstAttribute*/) override {
+ visitor_->VisitStartTag(
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
+ element.Name()));
+ return true;
+ }
+
+ bool VisitExit(const tinyxml2::XMLElement& /*element*/) override {
+ visitor_->VisitEndTag();
+ return true;
+ }
+
+ private:
+ Visitor* visitor_;
+};
+
+// Returns whether a layout resource represented by a TinyXML document is supported by the layout
+// compiler.
+bool CanCompileLayout(const tinyxml2::XMLDocument& xml, std::string* message = nullptr);
+
+} // namespace startop
+
+#endif // TINYXML_LAYOUT_PARSER_H_
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index eaff50a..b61e99b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1198,7 +1198,8 @@
}
/**
- * Request a refresh of the platform cache of profile information.
+ * Request a refresh of the platform cache of profile information for the eUICC which
+ * corresponds to the card ID returned by {@link TelephonyManager#getCardIdForDefaultEuicc()}.
*
* <p>Should be called by the EuiccService implementation whenever this information changes due
* to an operation done outside the scope of a request initiated by the platform to the
@@ -1206,17 +1207,50 @@
* were made through the EuiccService.
*
* <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+ *
* @hide
*/
@SystemApi
public void requestEmbeddedSubscriptionInfoListRefresh() {
+ int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- iSub.requestEmbeddedSubscriptionInfoListRefresh();
+ iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
}
} catch (RemoteException ex) {
- // ignore it
+ logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
+ }
+ }
+
+ /**
+ * Request a refresh of the platform cache of profile information for the eUICC with the given
+ * {@code cardId}.
+ *
+ * <p>Should be called by the EuiccService implementation whenever this information changes due
+ * to an operation done outside the scope of a request initiated by the platform to the
+ * EuiccService. There is no need to refresh for downloads, deletes, or other operations that
+ * were made through the EuiccService.
+ *
+ * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @param cardId the card ID of the eUICC.
+ *
+ * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
+ }
+ } catch (RemoteException ex) {
+ logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
}
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 65eedb8..d169b7d 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -104,7 +104,7 @@
/**
* @see android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh
*/
- oneway void requestEmbeddedSubscriptionInfoListRefresh();
+ oneway void requestEmbeddedSubscriptionInfoListRefresh(int cardId);
/**
* Add a new SubscriptionInfo to subinfo database if needed
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index df34c73..ab9bd84 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -25,7 +25,6 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -48,7 +47,6 @@
import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
import android.net.dhcp.DhcpServer.Clock;
import android.net.dhcp.DhcpServer.Dependencies;
-import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
@@ -74,9 +72,6 @@
public class DhcpServerTest {
private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
private static final String TEST_IFACE = "testiface";
- private static final MacAddress TEST_IFACE_MAC = MacAddress.fromString("11:22:33:44:55:66");
- private static final InterfaceParams TEST_IFACEPARAMS =
- new InterfaceParams(TEST_IFACE, 1, TEST_IFACE_MAC);
private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
@@ -149,7 +144,7 @@
.build();
mLooper = new TestLooper();
- mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACEPARAMS, servingParams,
+ mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
mServer.start();
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index cff0b54..2c675c6 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -404,7 +404,7 @@
private void assertDhcpStarted(IpPrefix expectedPrefix) {
verify(mDependencies, times(1)).makeDhcpServer(
- eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
+ eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
verify(mDhcpServer, times(1)).start();
final DhcpServingParams params = mDhcpParamsCaptor.getValue();
// Last address byte is random
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index bca9be7..e6b43d2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -240,7 +240,7 @@
}
@Override
- public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ public DhcpServer makeDhcpServer(Looper looper, String ifName,
DhcpServingParams params, SharedLog log) {
return mDhcpServer;
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9587704..c91134c 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -78,7 +78,7 @@
static uint32_t ParseFormatAttribute(const StringPiece& str) {
uint32_t mask = 0;
- for (StringPiece part : util::Tokenize(str, '|')) {
+ for (const StringPiece& part : util::Tokenize(str, '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
uint32_t type = ParseFormatType(trimmed_part);
if (type == 0) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index da22e88..c6f9152 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -362,7 +362,7 @@
return util::make_unique<BinaryPrimitive>(flags);
}
- for (StringPiece part : util::Tokenize(str, '|')) {
+ for (const StringPiece& part : util::Tokenize(str, '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
bool flag_set = false;
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index fc9514a..f63a074 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -433,7 +433,7 @@
}
Printer r_txt_printer(&fout_text);
- for (const auto res : xmlres->file.exported_symbols) {
+ for (const auto& res : xmlres->file.exported_symbols) {
r_txt_printer.Print("default int id ");
r_txt_printer.Println(res.name.entry);
}
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 8d91b00..a4610b2 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -113,7 +113,7 @@
void AnnotationProcessor::Print(Printer* printer) const {
if (has_comments_) {
std::string result = comment_.str();
- for (StringPiece line : util::Tokenize(result, '\n')) {
+ for (const StringPiece& line : util::Tokenize(result, '\n')) {
printer->Println(line);
}
printer->Println(" */");
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 73105e16..5cfbbf2 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -182,7 +182,7 @@
std::string PackageToPath(const StringPiece& package) {
std::string out_path;
- for (StringPiece part : util::Tokenize(package, '.')) {
+ for (const StringPiece& part : util::Tokenize(package, '.')) {
AppendPath(&out_path, part);
}
return out_path;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 21d6b94..0362a1b 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -66,6 +66,8 @@
List<OsuProvider> getMatchingOsuProviders(in List<ScanResult> scanResult);
+ Map getMatchingPasspointConfigsForOsuProviders(in List<OsuProvider> osuProviders);
+
int addOrUpdateNetwork(in WifiConfiguration config, String packageName);
boolean addOrUpdatePasspointConfiguration(in PasspointConfiguration config, String packageName);
@@ -195,5 +197,7 @@
int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
String[] getFactoryMacAddresses();
+
+ void setDeviceMobilityState(int state);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 57c97ea..a7c2ff0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -57,8 +57,11 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
@@ -1230,6 +1233,30 @@
}
/**
+ * Returns the matching Passpoint R2 configurations for given OSU (Online Sign-Up) providers.
+ *
+ * Given a list of OSU providers, this only returns OSU providers that already have Passpoint R2
+ * configurations in the device.
+ * An empty map will be returned when there is no matching Passpoint R2 configuration for the
+ * given OsuProviders.
+ *
+ * @param osuProviders a set of {@link OsuProvider}
+ * @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}.
+ * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
+ @NonNull Set<OsuProvider> osuProviders) {
+ try {
+ return mService.getMatchingPasspointConfigsForOsuProviders(
+ new ArrayList<>(osuProviders));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Add a new network description to the set of configured networks.
* The {@code networkId} field of the supplied configuration object
* is ignored.
@@ -4449,4 +4476,69 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"DEVICE_MOBILITY_STATE_"}, value = {
+ DEVICE_MOBILITY_STATE_UNKNOWN,
+ DEVICE_MOBILITY_STATE_HIGH_MVMT,
+ DEVICE_MOBILITY_STATE_LOW_MVMT,
+ DEVICE_MOBILITY_STATE_STATIONARY})
+ public @interface DeviceMobilityState {}
+
+ /**
+ * Unknown device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0;
+
+ /**
+ * High movement device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1;
+
+ /**
+ * Low movement device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2;
+
+ /**
+ * Stationary device mobility state
+ *
+ * @see #setDeviceMobilityState(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3;
+
+ /**
+ * Updates the device mobility state. Wifi uses this information to adjust the interval between
+ * Wifi scans in order to balance power consumption with scan accuracy.
+ * @param state the updated device mobility state
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
+ public void setDeviceMobilityState(@DeviceMobilityState int state) {
+ try {
+ mService.setDeviceMobilityState(state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 893b19c..6d82ca1 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -19,13 +19,16 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.wifi.WifiSsid;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
import java.util.Objects;
/**
@@ -52,9 +55,9 @@
private WifiSsid mOsuSsid;
/**
- * Friendly name of the OSU provider.
+ * Map of friendly names expressed as different language for the OSU provider.
*/
- private final String mFriendlyName;
+ private final Map<String, String> mFriendlyNames;
/**
* Description of the OSU provider.
@@ -81,10 +84,11 @@
*/
private final Icon mIcon;
- public OsuProvider(WifiSsid osuSsid, String friendlyName, String serviceDescription,
- Uri serverUri, String nai, List<Integer> methodList, Icon icon) {
+ public OsuProvider(WifiSsid osuSsid, Map<String, String> friendlyNames,
+ String serviceDescription, Uri serverUri, String nai, List<Integer> methodList,
+ Icon icon) {
mOsuSsid = osuSsid;
- mFriendlyName = friendlyName;
+ mFriendlyNames = friendlyNames;
mServiceDescription = serviceDescription;
mServerUri = serverUri;
mNetworkAccessIdentifier = nai;
@@ -104,7 +108,7 @@
public OsuProvider(OsuProvider source) {
if (source == null) {
mOsuSsid = null;
- mFriendlyName = null;
+ mFriendlyNames = null;
mServiceDescription = null;
mServerUri = null;
mNetworkAccessIdentifier = null;
@@ -114,7 +118,7 @@
}
mOsuSsid = source.mOsuSsid;
- mFriendlyName = source.mFriendlyName;
+ mFriendlyNames = source.mFriendlyNames;
mServiceDescription = source.mServiceDescription;
mServerUri = source.mServerUri;
mNetworkAccessIdentifier = source.mNetworkAccessIdentifier;
@@ -134,8 +138,32 @@
mOsuSsid = osuSsid;
}
+ /**
+ * Return the friendly Name for current language from the list of friendly names of OSU
+ * provider.
+ *
+ * The string matching the default locale will be returned if it is found, otherwise the string
+ * in english or the first string in the list will be returned if english is not found.
+ * A null will be returned if the list is empty.
+ *
+ * @return String matching the default locale, null otherwise
+ */
public String getFriendlyName() {
- return mFriendlyName;
+ if (mFriendlyNames == null || mFriendlyNames.isEmpty()) return null;
+ String lang = Locale.getDefault().getLanguage();
+ String friendlyName = mFriendlyNames.get(lang);
+ if (friendlyName != null) {
+ return friendlyName;
+ }
+ friendlyName = mFriendlyNames.get("en");
+ if (friendlyName != null) {
+ return friendlyName;
+ }
+ return mFriendlyNames.get(mFriendlyNames.keySet().stream().findFirst().get());
+ }
+
+ public Map<String, String> getFriendlyNameList() {
+ return mFriendlyNames;
}
public String getServiceDescription() {
@@ -151,7 +179,7 @@
}
public List<Integer> getMethodList() {
- return Collections.unmodifiableList(mMethodList);
+ return mMethodList;
}
public Icon getIcon() {
@@ -166,12 +194,14 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mOsuSsid, flags);
- dest.writeString(mFriendlyName);
dest.writeString(mServiceDescription);
dest.writeParcelable(mServerUri, flags);
dest.writeString(mNetworkAccessIdentifier);
dest.writeList(mMethodList);
dest.writeParcelable(mIcon, flags);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable("friendlyNameMap", (HashMap<String, String>) mFriendlyNames);
+ dest.writeBundle(bundle);
}
@Override
@@ -184,7 +214,8 @@
}
OsuProvider that = (OsuProvider) thatObject;
return (mOsuSsid == null ? that.mOsuSsid == null : mOsuSsid.equals(that.mOsuSsid))
- && TextUtils.equals(mFriendlyName, that.mFriendlyName)
+ && (mFriendlyNames == null) ? that.mFriendlyNames == null
+ : mFriendlyNames.equals(that.mFriendlyNames)
&& TextUtils.equals(mServiceDescription, that.mServiceDescription)
&& (mServerUri == null ? that.mServerUri == null
: mServerUri.equals(that.mServerUri))
@@ -196,14 +227,15 @@
@Override
public int hashCode() {
- return Objects.hash(mOsuSsid, mFriendlyName, mServiceDescription, mServerUri,
- mNetworkAccessIdentifier, mMethodList, mIcon);
+ // mIcon is not hashable, skip the variable.
+ return Objects.hash(mOsuSsid, mServiceDescription, mFriendlyNames,
+ mServerUri, mNetworkAccessIdentifier, mMethodList);
}
@Override
public String toString() {
return "OsuProvider{mOsuSsid=" + mOsuSsid
- + " mFriendlyName=" + mFriendlyName
+ + " mFriendlyNames=" + mFriendlyNames
+ " mServiceDescription=" + mServiceDescription
+ " mServerUri=" + mServerUri
+ " mNetworkAccessIdentifier=" + mNetworkAccessIdentifier
@@ -212,20 +244,22 @@
}
public static final Creator<OsuProvider> CREATOR =
- new Creator<OsuProvider>() {
- @Override
- public OsuProvider createFromParcel(Parcel in) {
- WifiSsid osuSsid = (WifiSsid) in.readParcelable(null);
- String friendlyName = in.readString();
- String serviceDescription = in.readString();
- Uri serverUri = (Uri) in.readParcelable(null);
- String nai = in.readString();
- List<Integer> methodList = new ArrayList<>();
- in.readList(methodList, null);
- Icon icon = (Icon) in.readParcelable(null);
- return new OsuProvider(osuSsid, friendlyName, serviceDescription, serverUri,
- nai, methodList, icon);
- }
+ new Creator<OsuProvider>() {
+ @Override
+ public OsuProvider createFromParcel(Parcel in) {
+ WifiSsid osuSsid = in.readParcelable(null);
+ String serviceDescription = in.readString();
+ Uri serverUri = in.readParcelable(null);
+ String nai = in.readString();
+ List<Integer> methodList = new ArrayList<>();
+ in.readList(methodList, null);
+ Icon icon = in.readParcelable(null);
+ Bundle bundle = in.readBundle();
+ Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
+ "friendlyNameMap");
+ return new OsuProvider(osuSsid, friendlyNamesMap, serviceDescription,
+ serverUri, nai, methodList, icon);
+ }
@Override
public OsuProvider[] newArray(int size) {
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 26bdb18..f09d864 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -20,6 +20,7 @@
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -30,6 +31,7 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@@ -324,6 +326,50 @@
}
/**
+ * The map of OSU service provider names whose each element is presented in different
+ * languages for the service provider, which is used for finding a matching
+ * PasspointConfiguration with a given service provider name.
+ */
+ private Map<String, String> mServiceFriendlyNames = null;
+
+ /**
+ * @hide
+ */
+ public void setServiceFriendlyNames(Map<String, String> serviceFriendlyNames) {
+ mServiceFriendlyNames = serviceFriendlyNames;
+ }
+
+ /**
+ * @hide
+ */
+ public Map<String, String> getServiceFriendlyNames() {
+ return mServiceFriendlyNames;
+ }
+
+ /**
+ * Return the friendly Name for current language from the list of friendly names of OSU
+ * provider.
+ * The string matching the default locale will be returned if it is found, otherwise the
+ * first string in the list will be returned. A null will be returned if the list is empty.
+ *
+ * @return String matching the default locale, null otherwise
+ * @hide
+ */
+ public String getServiceFriendlyName() {
+ if (mServiceFriendlyNames == null || mServiceFriendlyNames.isEmpty()) return null;
+ String lang = Locale.getDefault().getLanguage();
+ String friendlyName = mServiceFriendlyNames.get(lang);
+ if (friendlyName != null) {
+ return friendlyName;
+ }
+ friendlyName = mServiceFriendlyNames.get("en");
+ if (friendlyName != null) {
+ return friendlyName;
+ }
+ return mServiceFriendlyNames.get(mServiceFriendlyNames.keySet().stream().findFirst().get());
+ }
+
+ /**
* Constructor for creating PasspointConfiguration with default values.
*/
public PasspointConfiguration() {}
@@ -362,6 +408,7 @@
mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis;
mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes;
mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
+ mServiceFriendlyNames = source.mServiceFriendlyNames;
}
@Override
@@ -385,6 +432,10 @@
dest.writeLong(mUsageLimitStartTimeInMillis);
dest.writeLong(mUsageLimitDataLimit);
dest.writeLong(mUsageLimitTimeLimitInMinutes);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable("serviceFriendlyNames",
+ (HashMap<String, String>) mServiceFriendlyNames);
+ dest.writeBundle(bundle);
}
@Override
@@ -398,10 +449,10 @@
PasspointConfiguration that = (PasspointConfiguration) thatObject;
return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp))
&& (mCredential == null ? that.mCredential == null
- : mCredential.equals(that.mCredential))
+ : mCredential.equals(that.mCredential))
&& (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy))
&& (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null
- : mSubscriptionUpdate.equals(that.mSubscriptionUpdate))
+ : mSubscriptionUpdate.equals(that.mSubscriptionUpdate))
&& isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList)
&& mUpdateIdentifier == that.mUpdateIdentifier
&& mCredentialPriority == that.mCredentialPriority
@@ -411,7 +462,9 @@
&& mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes
&& mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis
&& mUsageLimitDataLimit == that.mUsageLimitDataLimit
- && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes;
+ && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
+ && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
+ : mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
}
@Override
@@ -419,7 +472,8 @@
return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList,
mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
- mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes);
+ mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
+ mServiceFriendlyNames);
}
@Override
@@ -463,6 +517,9 @@
builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
.append("\n");
}
+ if (mServiceFriendlyNames != null) {
+ builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
+ }
return builder.toString();
}
@@ -562,6 +619,10 @@
config.setUsageLimitStartTimeInMillis(in.readLong());
config.setUsageLimitDataLimit(in.readLong());
config.setUsageLimitTimeLimitInMinutes(in.readLong());
+ Bundle bundle = in.readBundle();
+ Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
+ "serviceFriendlyNames");
+ config.setServiceFriendlyNames(friendlyNamesMap);
return config;
}
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 36f66aa..e94b9e6 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -39,6 +39,7 @@
import android.os.WorkSource;
import java.util.List;
+import java.util.Map;
/**
* Abstract class implementing IWifiManager with stub methods throwing runtime exceptions.
@@ -127,6 +128,12 @@
}
@Override
+ public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
+ List<OsuProvider> osuProviders) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public int addOrUpdateNetwork(WifiConfiguration config, String packageName) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
index d3f91f0..89ecd0f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
@@ -28,9 +28,10 @@
import org.junit.Test;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Unit tests for {@link android.net.wifi.hotspot2.OsuProvider}.
@@ -40,6 +41,15 @@
private static final WifiSsid TEST_SSID =
WifiSsid.createFromByteArray("TEST SSID".getBytes(StandardCharsets.UTF_8));
private static final String TEST_FRIENDLY_NAME = "Friendly Name";
+ private static final Map<String, String> TEST_FRIENDLY_NAMES =
+ new HashMap<String, String>() {
+ {
+ put("en", TEST_FRIENDLY_NAME);
+ put("kr", TEST_FRIENDLY_NAME + 2);
+ put("jp", TEST_FRIENDLY_NAME + 3);
+ }
+ };
+
private static final String TEST_SERVICE_DESCRIPTION = "Dummy Service";
private static final Uri TEST_SERVER_URI = Uri.parse("https://test.com");
private static final String TEST_NAI = "test.access.com";
@@ -59,7 +69,9 @@
parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
OsuProvider readInfo = OsuProvider.CREATOR.createFromParcel(parcel);
+
assertEquals(writeInfo, readInfo);
+ assertEquals(writeInfo.hashCode(), readInfo.hashCode());
}
/**
@@ -79,8 +91,8 @@
*/
@Test
public void verifyParcelWithFullProviderInfo() throws Exception {
- verifyParcel(new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME, TEST_SERVICE_DESCRIPTION,
- TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON));
+ verifyParcel(new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
+ TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON));
}
/**
@@ -100,8 +112,8 @@
*/
@Test
public void verifyCopyConstructorWithValidSource() throws Exception {
- OsuProvider source = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME, TEST_SERVICE_DESCRIPTION,
- TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+ OsuProvider source = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
+ TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
assertEquals(source, new OsuProvider(source));
}
@@ -112,10 +124,12 @@
*/
@Test
public void verifyGetters() throws Exception {
- OsuProvider provider = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME,
+ OsuProvider provider = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+
assertTrue(TEST_SSID.equals(provider.getOsuSsid()));
assertTrue(TEST_FRIENDLY_NAME.equals(provider.getFriendlyName()));
+ assertTrue(TEST_FRIENDLY_NAMES.equals(provider.getFriendlyNameList()));
assertTrue(TEST_SERVICE_DESCRIPTION.equals(provider.getServiceDescription()));
assertTrue(TEST_SERVER_URI.equals(provider.getServerUri()));
assertTrue(TEST_NAI.equals(provider.getNetworkAccessIdentifier()));
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 775ce21..ee5a75e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -166,6 +166,10 @@
config.setUsageLimitStartTimeInMillis(124214213);
config.setUsageLimitDataLimit(14121);
config.setUsageLimitTimeLimitInMinutes(78912);
+ Map<String, String> friendlyNames = new HashMap<>();
+ friendlyNames.put("en", "ServiceName1");
+ friendlyNames.put("kr", "ServiceName2");
+ config.setServiceFriendlyNames(friendlyNames);
return config;
}
@@ -206,6 +210,18 @@
}
/**
+ * Verify parcel read/write for a configuration that doesn't contain a list of service names.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutServiceNames() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.setServiceFriendlyNames(null);
+ verifyParcel(config);
+ }
+
+ /**
* Verify parcel read/write for a configuration that doesn't contain HomeSP.
*
* @throws Exception