Merge "Update input windows when moving display to top"
diff --git a/Android.bp b/Android.bp
index dc9c4d4..4e7a7b4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -521,6 +521,8 @@
"telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl",
"telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl",
"telecomm/java/com/android/internal/telecom/IInCallService.aidl",
+ "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl",
+ "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl",
"telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
"telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
"telephony/java/android/telephony/data/IDataService.aidl",
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index 5ff4ebc..5852044 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
apct-perftests-utils
LOCAL_PACKAGE_NAME := MultiUserPerfTests
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index adb316f..e96771c 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -25,7 +25,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.perftests.multiuser"/>
</manifest>
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
index d3a3ce5..ba33e64 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
@@ -18,9 +18,10 @@
import android.app.Activity;
import android.app.Instrumentation;
import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 855be08..2fdba0a 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -27,9 +27,10 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
@@ -50,7 +51,7 @@
* adb install -r \
* ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
* adb shell am instrument -e class android.multiuser.UserLifecycleTests \
- * -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
+ * -w com.android.perftests.multiuser/androidx.test.runner.AndroidJUnitRunner
*
* or
*
diff --git a/api/current.txt b/api/current.txt
index de4e824..df5c185 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3934,6 +3934,7 @@
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+ method public boolean isActivityStartAllowedOnDisplay(android.content.Context, int, android.content.Intent);
method public boolean isBackgroundRestricted();
method public deprecated boolean isInLockTaskMode();
method public boolean isLowRamDevice();
@@ -5730,7 +5731,6 @@
public final class NotificationChannelGroup implements android.os.Parcelable {
ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
- method public boolean canOverlayApps();
method public android.app.NotificationChannelGroup clone();
method public int describeContents();
method public java.util.List<android.app.NotificationChannel> getChannels();
@@ -5738,7 +5738,6 @@
method public java.lang.String getId();
method public java.lang.CharSequence getName();
method public boolean isBlocked();
- method public void setAllowAppOverlay(boolean);
method public void setDescription(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
@@ -5746,6 +5745,7 @@
public class NotificationManager {
method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
+ method public boolean areAppOverlaysAllowed();
method public boolean areNotificationsEnabled();
method public boolean canNotifyAsPackage(java.lang.String);
method public void cancel(int);
@@ -10268,6 +10268,7 @@
field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
field public static final java.lang.String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE";
field public static final java.lang.String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
+ field public static final java.lang.String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
field public static final java.lang.String CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE";
field public static final java.lang.String CATEGORY_TAB = "android.intent.category.TAB";
field public static final java.lang.String CATEGORY_TEST = "android.intent.category.TEST";
@@ -11302,6 +11303,7 @@
method public void updateSessionAppLabel(int, java.lang.CharSequence);
field public static final java.lang.String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED";
field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+ field public static final java.lang.String ACTION_SESSION_UPDATED = "android.content.pm.action.SESSION_UPDATED";
field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_SESSION = "android.content.pm.extra.SESSION";
@@ -11366,13 +11368,20 @@
method public android.net.Uri getReferrerUri();
method public int getSessionId();
method public long getSize();
+ method public int getStagedSessionErrorCode();
method public boolean isActive();
method public boolean isMultiPackage();
method public boolean isSealed();
+ method public boolean isSessionApplied();
+ method public boolean isSessionFailed();
+ method public boolean isSessionReady();
method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACTIVATION_FAILED = 2; // 0x2
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
field public static final int INVALID_ID = -1; // 0xffffffff
+ field public static final int NO_ERROR = 0; // 0x0
+ field public static final int VERIFICATION_FAILED = 1; // 0x1
}
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
@@ -14127,8 +14136,11 @@
public final class Insets {
method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
+ method public static android.graphics.Insets max(android.graphics.Insets, android.graphics.Insets);
+ method public static android.graphics.Insets min(android.graphics.Insets, android.graphics.Insets);
method public static android.graphics.Insets of(int, int, int, int);
method public static android.graphics.Insets of(android.graphics.Rect);
+ method public static android.graphics.Insets subtract(android.graphics.Insets, android.graphics.Insets);
field public static final android.graphics.Insets NONE;
field public final int bottom;
field public final int left;
@@ -23372,12 +23384,13 @@
method public android.media.AudioPresentation.Builder setProgramId(int);
}
- public class AudioRecord implements android.media.AudioRouting {
+ public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
method protected void finalize();
method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
+ method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -23402,6 +23415,7 @@
method public int read(float[], int, int, int);
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
+ method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
method public void release();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
@@ -23413,6 +23427,7 @@
method public void startRecording() throws java.lang.IllegalStateException;
method public void startRecording(android.media.MediaSyncEvent) throws java.lang.IllegalStateException;
method public void stop() throws java.lang.IllegalStateException;
+ method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
field public static final int ERROR = -1; // 0xffffffff
field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
@@ -23455,14 +23470,24 @@
public final class AudioRecordingConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioDeviceInfo getAudioDevice();
+ method public int getAudioSource();
method public int getClientAudioSessionId();
method public int getClientAudioSource();
+ method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getClientEffects();
method public android.media.AudioFormat getClientFormat();
+ method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getEffects();
method public android.media.AudioFormat getFormat();
+ method public boolean isClientSilenced();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
}
+ public abstract interface AudioRecordingMonitor {
+ method public abstract android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
+ method public abstract void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
+ method public abstract void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
+ }
+
public abstract interface AudioRouting {
method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
@@ -25464,11 +25489,12 @@
field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
}
- public class MediaRecorder implements android.media.AudioRouting {
+ public class MediaRecorder implements android.media.AudioRecordingMonitor android.media.AudioRouting {
ctor public MediaRecorder();
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method protected void finalize();
method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
+ method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
method public android.os.PersistableBundle getMetrics();
@@ -25477,6 +25503,7 @@
method public android.view.Surface getSurface();
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
+ method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
method public void release();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public void reset();
@@ -25512,6 +25539,7 @@
method public void setVideoSource(int) throws java.lang.IllegalStateException;
method public void start() throws java.lang.IllegalStateException;
method public void stop() throws java.lang.IllegalStateException;
+ method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
@@ -28217,7 +28245,7 @@
method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
method public boolean bindProcessToNetwork(android.net.Network);
method public android.net.Network getActiveNetwork();
- method public android.net.NetworkInfo getActiveNetworkInfo();
+ method public deprecated android.net.NetworkInfo getActiveNetworkInfo();
method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
method public android.net.Network[] getAllNetworks();
method public deprecated boolean getBackgroundDataSetting();
@@ -28228,7 +28256,7 @@
method public int getMultipathPreference(android.net.Network);
method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
method public deprecated android.net.NetworkInfo getNetworkInfo(int);
- method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
+ method public deprecated android.net.NetworkInfo getNetworkInfo(android.net.Network);
method public deprecated int getNetworkPreference();
method public byte[] getNetworkWatchlistConfigHash();
method public static deprecated android.net.Network getProcessDefaultNetwork();
@@ -28262,14 +28290,14 @@
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
- field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
- field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
+ field public static final deprecated java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
+ field public static final deprecated java.lang.String EXTRA_IS_FAILOVER = "isFailover";
field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
field public static final java.lang.String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType";
field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity";
- field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+ field public static final deprecated java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
field public static final java.lang.String EXTRA_REASON = "reason";
field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1
field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4
@@ -28421,6 +28449,7 @@
method public android.net.ProxyInfo getHttpProxy();
method public java.lang.String getInterfaceName();
method public java.util.List<android.net.LinkAddress> getLinkAddresses();
+ method public int getMtu();
method public java.lang.String getPrivateDnsServerName();
method public java.util.List<android.net.RouteInfo> getRoutes();
method public boolean isPrivateDnsActive();
@@ -28569,10 +28598,10 @@
field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
}
- public class NetworkInfo implements android.os.Parcelable {
+ public deprecated class NetworkInfo implements android.os.Parcelable {
method public int describeContents();
method public deprecated android.net.NetworkInfo.DetailedState getDetailedState();
- method public java.lang.String getExtraInfo();
+ method public deprecated java.lang.String getExtraInfo();
method public deprecated java.lang.String getReason();
method public deprecated android.net.NetworkInfo.State getState();
method public deprecated int getSubtype();
@@ -28588,7 +28617,7 @@
field public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
}
- public static final class NetworkInfo.DetailedState extends java.lang.Enum {
+ public static final deprecated class NetworkInfo.DetailedState extends java.lang.Enum {
method public static android.net.NetworkInfo.DetailedState valueOf(java.lang.String);
method public static final android.net.NetworkInfo.DetailedState[] values();
enum_constant public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
@@ -28606,7 +28635,7 @@
enum_constant public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
}
- public static final class NetworkInfo.State extends java.lang.Enum {
+ public static final deprecated class NetworkInfo.State extends java.lang.Enum {
method public static android.net.NetworkInfo.State valueOf(java.lang.String);
method public static final android.net.NetworkInfo.State[] values();
enum_constant public static final android.net.NetworkInfo.State CONNECTED;
@@ -35140,6 +35169,7 @@
public final class StorageVolume implements android.os.Parcelable {
method public deprecated android.content.Intent createAccessIntent(java.lang.String);
+ method public android.content.Intent createOpenDocumentTreeIntent();
method public int describeContents();
method public java.lang.String getDescription(android.content.Context);
method public java.lang.String getState();
@@ -38348,6 +38378,11 @@
field public static final java.lang.String VALUE = "value";
}
+ public static final class Settings.Panel {
+ field public static final java.lang.String ACTION_INTERNET_CONNECTIVITY = "android.settings.panel.action.INTERNET_CONNECTIVITY";
+ field public static final java.lang.String ACTION_VOLUME = "android.settings.panel.action.VOLUME";
+ }
+
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
ctor public Settings.Secure();
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
@@ -38665,22 +38700,23 @@
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String CURRENT = "current";
field public static final java.lang.String DEFAULT_SORT_ORDER = "name ASC";
- field public static final java.lang.String MCC = "mcc";
+ field public static final deprecated java.lang.String MCC = "mcc";
field public static final java.lang.String MMSC = "mmsc";
field public static final java.lang.String MMSPORT = "mmsport";
field public static final java.lang.String MMSPROXY = "mmsproxy";
- field public static final java.lang.String MNC = "mnc";
- field public static final java.lang.String MVNO_MATCH_DATA = "mvno_match_data";
- field public static final java.lang.String MVNO_TYPE = "mvno_type";
+ field public static final deprecated java.lang.String MNC = "mnc";
+ field public static final deprecated java.lang.String MVNO_MATCH_DATA = "mvno_match_data";
+ field public static final deprecated java.lang.String MVNO_TYPE = "mvno_type";
field public static final java.lang.String NAME = "name";
field public static final java.lang.String NETWORK_TYPE_BITMASK = "network_type_bitmask";
- field public static final java.lang.String NUMERIC = "numeric";
+ field public static final deprecated java.lang.String NUMERIC = "numeric";
field public static final java.lang.String PASSWORD = "password";
field public static final java.lang.String PORT = "port";
field public static final java.lang.String PROTOCOL = "protocol";
field public static final java.lang.String PROXY = "proxy";
field public static final java.lang.String ROAMING_PROTOCOL = "roaming_protocol";
field public static final java.lang.String SERVER = "server";
+ field public static final android.net.Uri SIM_APN_URI;
field public static final java.lang.String SUBSCRIPTION_ID = "sub_id";
field public static final java.lang.String TYPE = "type";
field public static final java.lang.String USER = "user";
@@ -41236,10 +41272,12 @@
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
method public int getState();
+ method public java.lang.CharSequence getSubtitle();
method public void setContentDescription(java.lang.CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(java.lang.CharSequence);
method public void setState(int);
+ method public void setSubtitle(java.lang.CharSequence);
method public void updateTile();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
@@ -43631,6 +43669,10 @@
field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT = "opportunistic_network_entry_threshold_rsrp_int";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int";
+ field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
field public static final java.lang.String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
@@ -52205,20 +52247,20 @@
}
public final class ContentCaptureManager {
- method public android.view.contentcapture.ContentCaptureSession createContentCaptureSession(android.view.contentcapture.ContentCaptureContext);
method public android.content.ComponentName getServiceComponentName();
method public boolean isContentCaptureEnabled();
method public void removeUserData(android.view.contentcapture.UserDataRemovalRequest);
method public void setContentCaptureEnabled(boolean);
}
- public final class ContentCaptureSession implements java.lang.AutoCloseable {
+ public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
method public void close();
- method public void destroy();
- method public android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
- method public void notifyViewAppeared(android.view.ViewStructure);
- method public void notifyViewDisappeared(android.view.autofill.AutofillId);
- method public void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
+ method public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(android.view.contentcapture.ContentCaptureContext);
+ method public final void destroy();
+ method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
+ method public final void notifyViewAppeared(android.view.ViewStructure);
+ method public final void notifyViewDisappeared(android.view.autofill.AutofillId);
+ method public final void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
field public static final int FLAG_USER_INPUT = 1; // 0x1
}
diff --git a/api/system-current.txt b/api/system-current.txt
index dfcae63..734ac82 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -27,6 +27,7 @@
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
+ field public static final java.lang.String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
@@ -1471,6 +1472,8 @@
public final class BrightnessConfiguration implements android.os.Parcelable {
method public int describeContents();
+ method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
+ method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
method public android.util.Pair<float[], float[]> getCurve();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1478,10 +1481,22 @@
public static class BrightnessConfiguration.Builder {
ctor public BrightnessConfiguration.Builder(float[], float[]);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
method public android.hardware.display.BrightnessConfiguration build();
+ method public int getMaxCorrectionsByCategory();
+ method public int getMaxCorrectionsByPackageName();
method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
}
+ public final class BrightnessCorrection implements android.os.Parcelable {
+ method public float apply(float);
+ method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
+ }
+
public final class DisplayManager {
method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -2520,7 +2535,37 @@
}
public class UsbManager {
+ method public java.util.List<android.hardware.usb.UsbPort> getPorts();
method public void grantPermission(android.hardware.usb.UsbDevice, java.lang.String);
+ field public static final java.lang.String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
+ }
+
+ public final class UsbPort {
+ method public android.hardware.usb.UsbPortStatus getStatus();
+ method public void setRoles(int, int);
+ }
+
+ public final class UsbPortStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCurrentDataRole();
+ method public int getCurrentMode();
+ method public int getCurrentPowerRole();
+ method public int getSupportedRoleCombinations();
+ method public boolean isConnected();
+ method public boolean isRoleCombinationSupported(int, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
+ field public static final int DATA_ROLE_DEVICE = 2; // 0x2
+ field public static final int DATA_ROLE_HOST = 1; // 0x1
+ field public static final int DATA_ROLE_NONE = 0; // 0x0
+ field public static final int MODE_AUDIO_ACCESSORY = 4; // 0x4
+ field public static final int MODE_DEBUG_ACCESSORY = 8; // 0x8
+ field public static final int MODE_DFP = 2; // 0x2
+ field public static final int MODE_NONE = 0; // 0x0
+ field public static final int MODE_UFP = 1; // 0x1
+ field public static final int POWER_ROLE_NONE = 0; // 0x0
+ field public static final int POWER_ROLE_SINK = 2; // 0x2
+ field public static final int POWER_ROLE_SOURCE = 1; // 0x1
}
}
@@ -2532,6 +2577,81 @@
method public void onLocationBatch(java.util.List<android.location.Location>);
}
+ public final class GnssMeasurementCorrections implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getAltitudeMeters();
+ method public double getLatitudeDegrees();
+ method public double getLongitudeDegrees();
+ method public java.util.List<android.location.GnssSingleSatCorrection> getSingleSatCorrectionList();
+ method public long getToaGpsNanosecondsOfWeek();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementCorrections> CREATOR;
+ }
+
+ public static class GnssMeasurementCorrections.Builder {
+ ctor public GnssMeasurementCorrections.Builder();
+ method public android.location.GnssMeasurementCorrections build();
+ method public android.location.GnssMeasurementCorrections.Builder setAltitudeMeters(double);
+ method public android.location.GnssMeasurementCorrections.Builder setLatitudeDegrees(double);
+ method public android.location.GnssMeasurementCorrections.Builder setLongitudeDegrees(double);
+ method public android.location.GnssMeasurementCorrections.Builder setSingleSatCorrectionList(java.util.List<android.location.GnssSingleSatCorrection>);
+ method public android.location.GnssMeasurementCorrections.Builder setToaGpsNanosecondsOfWeek(long);
+ }
+
+ public final class GnssReflectingPlane implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getAltitudeMeters();
+ method public double getAzimuthDegrees();
+ method public double getLatitudeDegrees();
+ method public double getLongitudeDegrees();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.location.GnssReflectingPlane> CREATOR;
+ }
+
+ public static class GnssReflectingPlane.Builder {
+ ctor public GnssReflectingPlane.Builder();
+ method public android.location.GnssReflectingPlane build();
+ method public android.location.GnssReflectingPlane.Builder setAltitudeMeters(double);
+ method public android.location.GnssReflectingPlane.Builder setAzimuthDegrees(double);
+ method public android.location.GnssReflectingPlane.Builder setLatitudeDegrees(double);
+ method public android.location.GnssReflectingPlane.Builder setLongitudeDegrees(double);
+ }
+
+ public final class GnssSingleSatCorrection implements android.os.Parcelable {
+ method public int describeContents();
+ method public float getCarrierFrequencyHz();
+ method public int getConstellationType();
+ method public float getExcessPathLengthMeters();
+ method public float getExcessPathLengthUncertaintyMeters();
+ method public android.location.GnssReflectingPlane getReflectingPlane();
+ method public int getSatId();
+ method public int getSingleSatCorrectionFlags();
+ method public boolean hasExcessPathLength();
+ method public boolean hasExcessPathLengthUncertainty();
+ method public boolean hasReflectingPlane();
+ method public boolean hasSatelliteLineOfSight();
+ method public boolean isSatelliteLineOfSight();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR;
+ field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2
+ field public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 4; // 0x4
+ field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8
+ field public static final int HAS_SAT_IS_LOS_MASK = 1; // 0x1
+ }
+
+ public static class GnssSingleSatCorrection.Builder {
+ ctor public GnssSingleSatCorrection.Builder();
+ method public android.location.GnssSingleSatCorrection build();
+ method public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(float);
+ method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int);
+ method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float);
+ method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float);
+ method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane);
+ method public android.location.GnssSingleSatCorrection.Builder setSatId(int);
+ method public android.location.GnssSingleSatCorrection.Builder setSatIsLos(boolean);
+ method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int);
+ }
+
public class GpsClock implements android.os.Parcelable {
method public int describeContents();
method public double getBiasInNs();
@@ -2768,8 +2888,10 @@
method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
method public void flushGnssBatch();
method public int getGnssBatchSize();
+ method public int getGnssCapabilities();
method public java.lang.String getLocationControllerExtraPackage();
method public java.lang.String getNetworkProviderPackage();
+ method public void injectGnssMeasurementCorrections(android.location.GnssMeasurementCorrections);
method public boolean isLocationControllerExtraPackageEnabled();
method public boolean isLocationEnabledForUser(android.os.UserHandle);
method public boolean isProviderEnabledForUser(java.lang.String, android.os.UserHandle);
@@ -2908,7 +3030,7 @@
field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
}
- public class AudioRecord implements android.media.AudioRouting {
+ public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
}
@@ -3396,7 +3518,20 @@
ctor public LinkAddress(java.lang.String);
}
+ public final class LinkProperties implements android.os.Parcelable {
+ ctor public LinkProperties();
+ method public boolean addRoute(android.net.RouteInfo);
+ method public void clear();
+ method public void setDnsServers(java.util.Collection<java.net.InetAddress>);
+ method public void setDomains(java.lang.String);
+ method public void setHttpProxy(android.net.ProxyInfo);
+ method public void setInterfaceName(java.lang.String);
+ method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>);
+ method public void setMtu(int);
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
+ method public int getSignalStrength();
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
}
@@ -3416,6 +3551,10 @@
method public abstract void onRequestScores(android.net.NetworkKey[]);
}
+ public static class NetworkRequest.Builder {
+ method public android.net.NetworkRequest.Builder setSignalStrength(int);
+ }
+
public class NetworkScoreManager {
method public boolean clearScores() throws java.lang.SecurityException;
method public void disableScoring() throws java.lang.SecurityException;
@@ -5005,7 +5144,7 @@
package android.service.contentcapture {
- public final class ContentCaptureEventsRequest implements android.os.Parcelable {
+ public final deprecated class ContentCaptureEventsRequest implements android.os.Parcelable {
method public int describeContents();
method public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents();
method public void writeToParcel(android.os.Parcel, int);
@@ -5017,9 +5156,12 @@
method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
- method public void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
+ method public void onConnected();
+ method public void onContentCaptureEvent(android.view.contentcapture.ContentCaptureSessionId, android.view.contentcapture.ContentCaptureEvent);
+ method public deprecated void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
+ method public void onDisconnected();
method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
method public final void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
method public final void setPackageContentCaptureEnabled(java.lang.String, boolean);
@@ -5039,6 +5181,16 @@
package android.service.euicc {
+ public final class DownloadSubscriptionResult implements android.os.Parcelable {
+ ctor public DownloadSubscriptionResult(int, int, int);
+ method public int describeContents();
+ method public int getCardId();
+ method public int getResolvableErrors();
+ method public int getResult();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.euicc.DownloadSubscriptionResult> CREATOR;
+ }
+
public final class EuiccProfileInfo implements android.os.Parcelable {
method public int describeContents();
method public android.service.carrier.CarrierIdentifier getCarrierIdentifier();
@@ -5092,7 +5244,8 @@
ctor public EuiccService();
method public android.os.IBinder onBind(android.content.Intent);
method public abstract int onDeleteSubscription(int, java.lang.String);
- method public abstract int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean);
+ method public abstract android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean, android.os.Bundle);
+ method public deprecated int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean);
method public abstract int onEraseSubscriptions(int);
method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
@@ -5107,19 +5260,25 @@
field public static final java.lang.String ACTION_BIND_CARRIER_PROVISIONING_SERVICE = "android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE";
field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
- field public static final java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
+ field public static final deprecated java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
field public static final java.lang.String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
field public static final java.lang.String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
+ field public static final java.lang.String ACTION_RESOLVE_RESOLVABLE_ERRORS = "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
field public static final java.lang.String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
+ field public static final java.lang.String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
+ field public static final java.lang.String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS";
+ field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1
+ field public static final int RESOLVABLE_ERROR_POLICY_RULES = 2; // 0x2
field public static final int RESULT_FIRST_USER = 1; // 0x1
field public static final int RESULT_MUST_DEACTIVATE_SIM = -1; // 0xffffffff
- field public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe
+ field public static final deprecated int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe
field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_RESOLVABLE_ERRORS = -2; // 0xfffffffe
}
public static abstract class EuiccService.OtaStatusChangedCallback {
@@ -5605,6 +5764,14 @@
ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
}
+ public class PhoneAccountSuggestionService extends android.app.Service {
+ ctor public PhoneAccountSuggestionService();
+ method public void onAccountSuggestionRequest(java.lang.String);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+ }
+
public final class RemoteConference {
method public deprecated void setAudioState(android.telecom.AudioState);
}
@@ -6096,7 +6263,7 @@
method public deprecated boolean getDataEnabled(int);
method public boolean getEmergencyCallbackMode();
method public java.lang.String getIsimDomain();
- method public int getPreferredNetworkType(int);
+ method public int getPreferredNetworkTypeBitmap();
method public int getRadioPowerState();
method public int getSimApplicationState();
method public int getSimCardState();
@@ -6125,6 +6292,7 @@
method public void setDataActivationState(int);
method public deprecated void setDataEnabled(int, boolean);
method public void setDataRoamingEnabled(boolean);
+ method public boolean setPreferredNetworkTypeBitmap(int);
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
method public void setSimPowerState(int);
@@ -6150,29 +6318,6 @@
field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
field public static final int INVALID_CARD_ID = -1; // 0xffffffff
field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
- field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
- field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
- field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
- field public static final int NETWORK_MODE_GLOBAL = 7; // 0x7
- field public static final int NETWORK_MODE_GSM_ONLY = 1; // 0x1
- field public static final int NETWORK_MODE_GSM_UMTS = 3; // 0x3
- field public static final int NETWORK_MODE_LTE_CDMA_EVDO = 8; // 0x8
- field public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; // 0xa
- field public static final int NETWORK_MODE_LTE_GSM_WCDMA = 9; // 0x9
- field public static final int NETWORK_MODE_LTE_ONLY = 11; // 0xb
- field public static final int NETWORK_MODE_LTE_TDSCDMA = 15; // 0xf
- field public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; // 0x16
- field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; // 0x11
- field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; // 0x14
- field public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; // 0x13
- field public static final int NETWORK_MODE_LTE_WCDMA = 12; // 0xc
- field public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; // 0x15
- field public static final int NETWORK_MODE_TDSCDMA_GSM = 16; // 0x10
- field public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; // 0x12
- field public static final int NETWORK_MODE_TDSCDMA_ONLY = 13; // 0xd
- field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
- field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
- field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
field public static final int NETWORK_TYPE_BITMASK_1xRTT = 128; // 0x80
field public static final int NETWORK_TYPE_BITMASK_CDMA = 16; // 0x10
field public static final int NETWORK_TYPE_BITMASK_EDGE = 4; // 0x4
@@ -6408,12 +6553,17 @@
method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method public int getOtaStatus();
field public static final java.lang.String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+ field public static final java.lang.String ACTION_PROFILE_SELECTION = "android.telephony.euicc.action.PROFILE_SELECTION";
field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
+ field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
+ field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
field public static final int EUICC_OTA_FAILED = 2; // 0x2
field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1
field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
+ field public static final java.lang.String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE";
field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
field public static final java.lang.String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION";
}
@@ -7497,6 +7647,7 @@
method public int getDisplayId();
method public android.os.Bundle getExtras();
method public int getFlags();
+ method public android.view.contentcapture.ContentCaptureSessionId getParentSessionId();
method public int getTaskId();
method public android.net.Uri getUri();
field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
diff --git a/api/test-current.txt b/api/test-current.txt
index 46e7683..71a06f1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -182,7 +182,6 @@
method public int getUserLockedFields();
method public void lockFields(int);
method public void setBlocked(boolean);
- field public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 2; // 0x2
}
public class NotificationManager {
@@ -639,6 +638,11 @@
method public static boolean isEncodingLinearPcm(int);
}
+ public final class AudioRecordingConfiguration implements android.os.Parcelable {
+ ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]);
+ ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String);
+ }
+
public final class BufferingParams implements android.os.Parcelable {
method public int describeContents();
method public int getInitialMarkMs();
@@ -1282,6 +1286,14 @@
ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
}
+ public class PhoneAccountSuggestionService extends android.app.Service {
+ ctor public PhoneAccountSuggestionService();
+ method public void onAccountSuggestionRequest(java.lang.String);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+ }
+
}
package android.telephony {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index e3748f1..3defdc5 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -247,7 +247,7 @@
}
try {
- mBmgr.dataChanged(pkg);
+ mBmgr.dataChangedForUser(userId, pkg);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -264,7 +264,8 @@
}
if (allPkgs.size() > 0) {
try {
- mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()]));
+ mBmgr.fullTransportBackupForUser(
+ userId, allPkgs.toArray(new String[allPkgs.size()]));
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -393,7 +394,7 @@
installedPackages.stream().map(p -> p.packageName).toArray(String[]::new);
String[] filteredPackages = {};
try {
- filteredPackages = mBmgr.filterAppsEligibleForBackup(packages);
+ filteredPackages = mBmgr.filterAppsEligibleForBackupForUser(userId, packages);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -498,11 +499,11 @@
}
if ("-c".equals(which)) {
- doTransportByComponent();
+ doTransportByComponent(userId);
return;
}
- String old = mBmgr.selectBackupTransport(which);
+ String old = mBmgr.selectBackupTransportForUser(userId, which);
if (old == null) {
System.out.println("Unknown transport '" + which
+ "' specified; no changes made.");
@@ -516,7 +517,7 @@
}
}
- private void doTransportByComponent() {
+ private void doTransportByComponent(@UserIdInt int userId) {
String which = nextArg();
if (which == null) {
showUsage();
@@ -526,7 +527,9 @@
final CountDownLatch latch = new CountDownLatch(1);
try {
- mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
+ mBmgr.selectBackupTransportAsyncForUser(
+ userId,
+ ComponentName.unflattenFromString(which),
new ISelectBackupTransportCallback.Stub() {
@Override
public void onSuccess(String transportName) {
@@ -567,7 +570,7 @@
}
try {
- mBmgr.clearBackupData(transport, pkg);
+ mBmgr.clearBackupDataForUser(userId, transport, pkg);
System.out.println("Wiped backup data for " + pkg + " on " + transport);
} catch (RemoteException e) {
System.err.println(e.toString());
@@ -599,7 +602,8 @@
InitObserver observer = new InitObserver();
try {
System.out.println("Initializing transports: " + transports);
- mBmgr.initializeTransports(transports.toArray(new String[transports.size()]), observer);
+ mBmgr.initializeTransportsForUser(
+ userId, transports.toArray(new String[transports.size()]), observer);
observer.waitForCompletion(30*1000L);
System.out.println("Initialization result: " + observer.result);
} catch (RemoteException e) {
@@ -611,13 +615,13 @@
private void doList(@UserIdInt int userId) {
String arg = nextArg(); // sets, transports, packages set#
if ("transports".equals(arg)) {
- doListTransports();
+ doListTransports(userId);
return;
}
// The rest of the 'list' options work with a restore session on the current transport
try {
- mRestore = mBmgr.beginRestoreSession(null, null);
+ mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
@@ -634,19 +638,19 @@
}
}
- private void doListTransports() {
+ private void doListTransports(@UserIdInt int userId) {
String arg = nextArg();
try {
if ("-c".equals(arg)) {
- for (ComponentName transport : mBmgr.listAllTransportComponents()) {
+ for (ComponentName transport : mBmgr.listAllTransportComponentsForUser(userId)) {
System.out.println(transport.flattenToShortString());
}
return;
}
- String current = mBmgr.getCurrentTransport();
- String[] transports = mBmgr.listAllTransports();
+ String current = mBmgr.getCurrentTransportForUser(userId);
+ String[] transports = mBmgr.listAllTransportsForUser(userId);
if (transports == null || transports.length == 0) {
System.out.println("No transports available.");
return;
@@ -756,7 +760,7 @@
filter.add(arg);
}
- doRestoreAll(token, filter);
+ doRestoreAll(userId, token, filter);
} catch (NumberFormatException e) {
showUsage();
return;
@@ -769,12 +773,12 @@
System.err.println("'restore <token> <package>'.");
}
- private void doRestoreAll(long token, HashSet<String> filter) {
+ private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter) {
RestoreObserver observer = new RestoreObserver();
try {
boolean didRestore = false;
- mRestore = mBmgr.beginRestoreSession(null, null);
+ mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index d7922bc..fe7099b 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -12,14 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-cc_library {
- name: "libidmap2",
- host_supported: true,
+cc_defaults {
+ name: "idmap2_defaults",
tidy: true,
+ tidy_checks: [
+ "android-*",
+ "misc-*",
+ "modernize-*",
+ "readability-*",
+ ],
tidy_flags: [
"-system-headers",
-// b/120024673 "-warnings-as-errors=*",
],
+}
+
+cc_library {
+ name: "libidmap2",
+ defaults: [
+ "idmap2_defaults",
+ ],
+ host_supported: true,
srcs: [
"libidmap2/BinaryStreamVisitor.cpp",
"libidmap2/CommandLineOptions.cpp",
@@ -60,12 +72,13 @@
cc_test {
name: "idmap2_tests",
- host_supported: true,
- tidy: true,
- tidy_flags: [
- "-system-headers",
-// b/120024673 "-warnings-as-errors=*",
+ defaults: [
+ "idmap2_defaults",
],
+ tidy_checks: [
+ "-readability-magic-numbers",
+ ],
+ host_supported: true,
srcs: [
"tests/BinaryStreamVisitorTests.cpp",
"tests/CommandLineOptionsTests.cpp",
@@ -114,12 +127,10 @@
cc_binary {
name: "idmap2",
- host_supported: true,
- tidy: true,
- tidy_flags: [
- "-system-headers",
-// b/120024673 "-warnings-as-errors=*",
+ defaults: [
+ "idmap2_defaults",
],
+ host_supported: true,
srcs: [
"idmap2/Create.cpp",
"idmap2/Dump.cpp",
@@ -156,19 +167,11 @@
cc_binary {
name: "idmap2d",
+ defaults: [
+ "idmap2_defaults",
+ ],
host_supported: false,
- tidy: true,
- tidy_checks: [
- // remove google-default-arguments or clang-tidy will complain about
- // the auto-generated file IIdmap2.cpp
- "-google-default-arguments",
- ],
- tidy_flags: [
- "-system-headers",
-// b/120024673 "-warnings-as-errors=*",
- ],
srcs: [
- ":idmap2_aidl",
"idmap2d/Idmap2Service.cpp",
"idmap2d/Main.cpp",
],
@@ -181,9 +184,30 @@
"libutils",
"libziparchive",
],
+ static_libs: [
+ "libidmap2daidl",
+ ],
init_rc: ["idmap2d/idmap2d.rc"],
}
+cc_library_static {
+ name: "libidmap2daidl",
+ defaults: [
+ "idmap2_defaults",
+ ],
+ tidy: false,
+ host_supported: false,
+ srcs: [
+ ":idmap2_aidl",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ aidl: {
+ export_aidl_headers: true,
+ },
+}
+
filegroup {
name: "idmap2_aidl",
srcs: [
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index 291eaeb..b075673 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -32,9 +32,12 @@
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Idmap;
+using android::idmap2::utils::kIdmapFilePermissionMask;
bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
- std::string target_apk_path, overlay_apk_path, idmap_path;
+ std::string target_apk_path;
+ std::string overlay_apk_path;
+ std::string idmap_path;
const CommandLineOptions opts =
CommandLineOptions("idmap2 create")
@@ -68,7 +71,7 @@
return false;
}
- umask(0133); // u=rw,g=r,o=r
+ umask(kIdmapFilePermissionMask);
std::ofstream fout(idmap_path);
if (fout.fail()) {
out_error << "failed to open idmap path " << idmap_path << std::endl;
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 8d0cee5..cfb5dd5 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -63,10 +63,12 @@
Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res,
const std::string& fallback_package) {
+ static constexpr const int kBaseHex = 16;
+
// first, try to parse as a hex number
char* endptr = nullptr;
ResourceId resid;
- resid = strtol(res.c_str(), &endptr, 16);
+ resid = strtol(res.c_str(), &endptr, kBaseHex);
if (*endptr == '\0') {
return {resid};
}
@@ -155,7 +157,9 @@
bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) {
std::vector<std::string> idmap_paths;
- std::string config_str, resid_str;
+ std::string config_str;
+ std::string resid_str;
+
const CommandLineOptions opts =
CommandLineOptions("idmap2 lookup")
.MandatoryOption("--idmap-path", "input: path to idmap file to load", &idmap_paths)
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index 5d9ea77..445fac5 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -29,10 +29,12 @@
using android::idmap2::CommandLineOptions;
-typedef std::map<std::string, std::function<int(const std::vector<std::string>&, std::ostream&)>>
- NameToFunctionMap;
+using NameToFunctionMap =
+ std::map<std::string, std::function<bool(const std::vector<std::string>&, std::ostream&)>>;
-static void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
+namespace {
+
+void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
out << "usage: idmap2 [";
for (auto iter = commands.cbegin(); iter != commands.cend(); iter++) {
if (iter != commands.cbegin()) {
@@ -43,6 +45,8 @@
out << "]" << std::endl;
}
+} // namespace
+
int main(int argc, char** argv) {
const NameToFunctionMap commands = {
{"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify},
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 00c49e3..4918747 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -44,7 +44,7 @@
const auto predicate = [](unsigned char type, const std::string& path) -> bool {
static constexpr size_t kExtLen = 4; // strlen(".apk")
return type == DT_REG && path.size() > kExtLen &&
- !path.compare(path.size() - kExtLen, kExtLen, ".apk");
+ path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0;
};
// pass apk paths through a set to filter out duplicates
std::set<std::string> paths;
@@ -56,14 +56,15 @@
}
paths.insert(apk_paths->cbegin(), apk_paths->cend());
}
- return std::unique_ptr<std::vector<std::string>>(
- new std::vector<std::string>(paths.cbegin(), paths.cend()));
+ return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
}
} // namespace
bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
std::vector<std::string> input_directories;
- std::string target_package_name, target_apk_path, output_directory;
+ std::string target_package_name;
+ std::string target_apk_path;
+ std::string output_directory;
bool recursive = false;
const CommandLineOptions opts =
@@ -112,7 +113,7 @@
}
auto iter = tag->find("isStatic");
- if (iter == tag->end() || std::stoul(iter->second) == 0u) {
+ if (iter == tag->end() || std::stoul(iter->second) == 0U) {
continue;
}
@@ -136,15 +137,15 @@
}
std::stringstream stream;
- for (auto iter = interesting_apks.cbegin(); iter != interesting_apks.cend(); ++iter) {
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, *iter);
+ for (const auto& apk : interesting_apks) {
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, apk);
std::stringstream dev_null;
if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), dev_null) &&
!Create(std::vector<std::string>({
"--target-apk-path",
target_apk_path,
"--overlay-apk-path",
- *iter,
+ apk,
"--idmap-path",
idmap_path,
}),
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/Verify.cpp
index b5fa438..4d4a0e7 100644
--- a/cmds/idmap2/idmap2/Verify.cpp
+++ b/cmds/idmap2/idmap2/Verify.cpp
@@ -27,6 +27,7 @@
bool Verify(const std::vector<std::string>& args, std::ostream& out_error) {
std::string idmap_path;
+
const CommandLineOptions opts =
CommandLineOptions("idmap2 verify")
.MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path);
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 86b00f1..d2e46e1 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -39,10 +39,11 @@
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
+using android::idmap2::utils::kIdmapFilePermissionMask;
namespace {
-static constexpr const char* kIdmapCacheDir = "/data/resource-cache";
+constexpr const char* kIdmapCacheDir = "/data/resource-cache";
Status ok() {
return Status::ok();
@@ -55,8 +56,7 @@
} // namespace
-namespace android {
-namespace os {
+namespace android::os {
Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) {
@@ -69,13 +69,12 @@
int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
assert(_aidl_return);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
- if (unlink(idmap_path.c_str()) == 0) {
- *_aidl_return = true;
- return ok();
- } else {
+ if (unlink(idmap_path.c_str()) != 0) {
*_aidl_return = false;
return error("failed to unlink " + idmap_path + ": " + strerror(errno));
}
+ *_aidl_return = true;
+ return ok();
}
Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
@@ -119,7 +118,7 @@
return error(err.str());
}
- umask(0133); // u=rw,g=r,o=r
+ umask(kIdmapFilePermissionMask);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ofstream fout(idmap_path);
if (fout.fail()) {
@@ -132,9 +131,8 @@
return error("failed to write to idmap path " + idmap_path);
}
- _aidl_return->reset(new std::string(idmap_path));
+ *_aidl_return = std::make_unique<std::string>(idmap_path);
return ok();
}
-} // namespace os
-} // namespace android
+} // namespace android::os
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 4e5abc9..e0bc22e 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -25,8 +25,8 @@
#include "android/os/BnIdmap2.h"
-namespace android {
-namespace os {
+namespace android::os {
+
class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
public:
static char const* getServiceName() {
@@ -46,7 +46,7 @@
const std::string& overlay_apk_path, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return);
};
-} // namespace os
-} // namespace android
+
+} // namespace android::os
#endif // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp
index d64a87b..4393dcc 100644
--- a/cmds/idmap2/idmap2d/Main.cpp
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -37,7 +37,7 @@
using android::os::Idmap2Service;
int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) {
- IPCThreadState::self()->disableBackgroundScheduling(true);
+ IPCThreadState::disableBackgroundScheduling(true);
status_t ret = BinderService<Idmap2Service>::publish();
if (ret != android::OK) {
return EXIT_FAILURE;
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index 2368aea..2c3e9d3 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -23,8 +23,7 @@
#include "idmap2/Idmap.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
class BinaryStreamVisitor : public Visitor {
public:
@@ -43,7 +42,6 @@
std::ostream& stream_;
};
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h
index f3aa68b..b93e716 100644
--- a/cmds/idmap2/include/idmap2/CommandLineOptions.h
+++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h
@@ -23,8 +23,7 @@
#include <string>
#include <vector>
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
/*
* Utility class to convert a command line, including options (--path foo.txt),
@@ -65,7 +64,6 @@
std::string name_;
};
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 05c6d31..5c41c49 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -22,9 +22,10 @@
#include <string>
#include <vector>
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
+
+constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
+
typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
FindFilesPredicate;
std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
@@ -34,8 +35,6 @@
std::unique_ptr<std::string> ReadFile(const std::string& path);
-} // namespace utils
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2::utils
#endif // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 837b7c5..b989e4c 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -57,8 +57,7 @@
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
class Idmap;
class Visitor;
@@ -271,7 +270,6 @@
virtual void visit(const IdmapData::TypeEntry& type_entry) = 0;
};
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index d106f19..323796b 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -25,14 +25,10 @@
#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
-} // namespace utils
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2::utils
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
index 9ab5ec4..dd89dee 100644
--- a/cmds/idmap2/include/idmap2/Xml.h
+++ b/cmds/idmap2/include/idmap2/Xml.h
@@ -25,8 +25,7 @@
#include "androidfw/ResourceTypes.h"
#include "utils/String16.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
class Xml {
public:
@@ -45,7 +44,6 @@
DISALLOW_COPY_AND_ASSIGN(Xml);
};
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
index 9edbbe0..8f50e36 100644
--- a/cmds/idmap2/include/idmap2/ZipFile.h
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -21,11 +21,10 @@
#include <string>
#include "android-base/macros.h"
-#include "ziparchive/zip_archive.h"
#include "idmap2/Result.h"
+#include "ziparchive/zip_archive.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
struct MemoryChunk {
size_t size;
@@ -56,7 +55,6 @@
DISALLOW_COPY_AND_ASSIGN(ZipFile);
};
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 29969a2..9651328 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -22,8 +22,7 @@
#include "idmap2/BinaryStreamVisitor.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
void BinaryStreamVisitor::Write16(uint16_t value) {
uint16_t x = htodl(value);
@@ -64,18 +63,17 @@
Write16(header.GetTypeCount());
}
-void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& te) {
- const uint16_t entryCount = te.GetEntryCount();
+void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) {
+ const uint16_t entryCount = type_entry.GetEntryCount();
- Write16(te.GetTargetTypeId());
- Write16(te.GetOverlayTypeId());
+ Write16(type_entry.GetTargetTypeId());
+ Write16(type_entry.GetOverlayTypeId());
Write16(entryCount);
- Write16(te.GetEntryOffset());
+ Write16(type_entry.GetEntryOffset());
for (uint16_t i = 0; i < entryCount; i++) {
- EntryId entry_id = te.GetEntry(i);
+ EntryId entry_id = type_entry.GetEntry(i);
Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding);
}
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index 28c3797..cabc8f3 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -26,13 +26,11 @@
#include "idmap2/CommandLineOptions.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
int argc, const char** argv) {
- return std::unique_ptr<std::vector<std::string>>(
- new std::vector<std::string>(argv + 1, argv + argc));
+ return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
}
CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
@@ -111,8 +109,8 @@
}
if (!mandatory_opts.empty()) {
- for (auto iter = mandatory_opts.cbegin(); iter != mandatory_opts.cend(); ++iter) {
- outError << "error: " << *iter << ": missing mandatory option" << std::endl;
+ for (const auto& opt : mandatory_opts) {
+ outError << "error: " << opt << ": missing mandatory option" << std::endl;
}
Usage(outError);
return false;
@@ -159,5 +157,4 @@
}
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 4ac4c04..0255727 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -15,10 +15,10 @@
*/
#include <dirent.h>
-#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
+#include <cerrno>
#include <fstream>
#include <memory>
#include <string>
@@ -27,19 +27,17 @@
#include "idmap2/FileUtils.h"
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
const FindFilesPredicate& predicate) {
DIR* dir = opendir(root.c_str());
- if (!dir) {
+ if (dir == nullptr) {
return nullptr;
}
std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
struct dirent* dirent;
- while ((dirent = readdir(dir))) {
+ while ((dirent = readdir(dir)) != nullptr) {
const std::string path = root + "/" + dirent->d_name;
if (predicate(dirent->d_type, path)) {
vector->push_back(path);
@@ -68,8 +66,10 @@
}
std::unique_ptr<std::string> ReadFile(int fd) {
+ static constexpr const size_t kBufSize = 1024;
+
std::unique_ptr<std::string> str(new std::string());
- char buf[1024];
+ char buf[kBufSize];
ssize_t r;
while ((r = read(fd, buf, sizeof(buf))) > 0) {
str->append(buf, r);
@@ -77,6 +77,4 @@
return r == 0 ? std::move(str) : nullptr;
}
-} // namespace utils
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 1ef3267..37d6af8 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -36,27 +36,34 @@
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
+
+namespace {
#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
-struct MatchingResources {
+class MatchingResources {
+ public:
void Add(ResourceId target_resid, ResourceId overlay_resid) {
TypeId target_typeid = EXTRACT_TYPE(target_resid);
- if (map.find(target_typeid) == map.end()) {
- map.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
+ if (map_.find(target_typeid) == map_.end()) {
+ map_.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
}
- map[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
+ map_[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
}
+ inline const std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>>& Map() const {
+ return map_;
+ }
+
+ private:
// target type id -> set { pair { overlay entry id, overlay entry id } }
- std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map;
+ std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map_;
};
-static bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
+bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
uint16_t value;
if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
*out = dtohl(value);
@@ -65,7 +72,7 @@
return false;
}
-static bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
+bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
uint32_t value;
if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
*out = dtohl(value);
@@ -75,7 +82,7 @@
}
// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
-static bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
+bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
char buf[kIdmapStringLength];
memset(buf, 0, sizeof(buf));
if (!stream.read(buf, sizeof(buf))) {
@@ -88,7 +95,7 @@
return true;
}
-static ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
+ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
return am.GetResourceId(name);
}
@@ -101,7 +108,7 @@
// relying on a hard-coded index. This however requires storing the package name in the idmap
// header, which in turn requires incrementing the idmap version. Because the initial version of
// idmap2 is compatible with idmap, this will have to wait for now.
-static const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
if (packages.empty()) {
return nullptr;
@@ -110,6 +117,8 @@
return loaded_arsc.GetPackageById(id);
}
+} // namespace
+
std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
@@ -196,8 +205,9 @@
std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
std::istream& stream) {
std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
-
- uint16_t target_type16, overlay_type16, entry_count;
+ uint16_t target_type16;
+ uint16_t overlay_type16;
+ uint16_t entry_count;
if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
!Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
return nullptr;
@@ -282,25 +292,25 @@
}
const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
- if (!target_arsc) {
+ if (target_arsc == nullptr) {
out_error << "error: failed to load target resources.arsc" << std::endl;
return nullptr;
}
const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
- if (!overlay_arsc) {
+ if (overlay_arsc == nullptr) {
out_error << "error: failed to load overlay resources.arsc" << std::endl;
return nullptr;
}
const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
- if (!target_pkg) {
+ if (target_pkg == nullptr) {
out_error << "error: failed to load target package from resources.arsc" << std::endl;
return nullptr;
}
const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
- if (!overlay_pkg) {
+ if (overlay_pkg == nullptr) {
out_error << "error: failed to load overlay package from resources.arsc" << std::endl;
return nullptr;
}
@@ -375,8 +385,8 @@
// encode idmap data
std::unique_ptr<IdmapData> data(new IdmapData());
- const auto types_end = matching_resources.map.cend();
- for (auto ti = matching_resources.map.cbegin(); ti != types_end; ++ti) {
+ const auto types_end = matching_resources.Map().cend();
+ for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
auto ei = ti->second.cbegin();
std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
type->target_type_id_ = EXTRACT_TYPE(ei->first);
@@ -439,5 +449,4 @@
}
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index fb3bc5b..fc967799 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -24,8 +24,7 @@
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
@@ -49,17 +48,18 @@
last_seen_package_id_ = header.GetTargetPackageId();
}
-void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
- for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
- const EntryId entry = te.GetEntry(i);
+ for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
+ const EntryId entry = type_entry.GetEntry(i);
if (entry == kNoEntry) {
continue;
}
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);
+ RESID(last_seen_package_id_, type_entry.GetTargetTypeId(), type_entry.GetEntryOffset() + i);
+ const ResourceId overlay_resid =
+ RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
if (target_package_loaded) {
@@ -72,5 +72,4 @@
}
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 7c24445..ec2decf 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -27,8 +27,7 @@
using android::ApkAssets;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
// verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils
#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
@@ -59,22 +58,23 @@
last_seen_package_id_ = header.GetTargetPackageId();
}
-void RawPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
- print(static_cast<uint16_t>(te.GetTargetTypeId()), "target type");
- print(static_cast<uint16_t>(te.GetOverlayTypeId()), "overlay type");
- print(static_cast<uint16_t>(te.GetEntryCount()), "entry count");
- print(static_cast<uint16_t>(te.GetEntryOffset()), "entry offset");
+ print(static_cast<uint16_t>(type_entry.GetTargetTypeId()), "target type");
+ print(static_cast<uint16_t>(type_entry.GetOverlayTypeId()), "overlay type");
+ print(static_cast<uint16_t>(type_entry.GetEntryCount()), "entry count");
+ print(static_cast<uint16_t>(type_entry.GetEntryOffset()), "entry offset");
- for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
- const EntryId entry = te.GetEntry(i);
+ for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
+ const EntryId entry = type_entry.GetEntry(i);
if (entry == kNoEntry) {
print(kPadding, "no entry");
} else {
- 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);
+ const ResourceId target_resid = RESID(last_seen_package_id_, type_entry.GetTargetTypeId(),
+ type_entry.GetEntryOffset() + i);
+ const ResourceId overlay_resid =
+ RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
Result<std::string> name;
if (target_package_loaded) {
name = utils::ResToTypeEntryName(target_am_, target_resid);
@@ -126,5 +126,4 @@
offset_ += kIdmapStringLength;
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index 5c89783..04e0ec7 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -25,9 +25,7 @@
using android::StringPiece16;
using android::util::Utf16ToUtf8;
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
AssetManager2::ResourceName name;
@@ -49,6 +47,4 @@
return {out};
}
-} // namespace utils
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
index 5543722..0075a92 100644
--- a/cmds/idmap2/libidmap2/Xml.cpp
+++ b/cmds/idmap2/libidmap2/Xml.cpp
@@ -21,8 +21,7 @@
#include "idmap2/Xml.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
std::unique_ptr<Xml> xml(new Xml());
@@ -78,5 +77,4 @@
xml_.uninit();
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 9fb611d..15ec3f9 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -20,8 +20,7 @@
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) {
void* ptr = ::operator new(sizeof(MemoryChunk) + size);
@@ -63,5 +62,4 @@
return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError;
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 8b552dc..2698ac0 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -29,11 +29,9 @@
#include "TestHelpers.h"
-using ::testing::IsNull;
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
@@ -52,14 +50,14 @@
ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc());
ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
- ASSERT_EQ(idmap1->GetData().size(), 1u);
+ ASSERT_EQ(idmap1->GetData().size(), 1U);
ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
const auto& data1 = idmap1->GetData()[0];
const auto& data2 = idmap2->GetData()[0];
ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
- ASSERT_EQ(data1->GetTypeEntries().size(), 2u);
+ ASSERT_EQ(data1->GetTypeEntries().size(), 2U);
ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
@@ -125,5 +123,4 @@
ASSERT_FALSE(success);
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
index b04b256..c27d27a 100644
--- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp
+++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
@@ -38,14 +38,11 @@
#include "TestHelpers.h"
-using ::testing::NotNull;
-
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
TEST(CommandLineOptionsTests, Flag) {
- bool foo = true, bar = false;
-
+ bool foo = true;
+ bool bar = false;
CommandLineOptions opts =
CommandLineOptions("test").OptionalFlag("--foo", "", &foo).OptionalFlag("--bar", "", &bar);
@@ -63,7 +60,8 @@
}
TEST(CommandLineOptionsTests, MandatoryOption) {
- std::string foo, bar;
+ std::string foo;
+ std::string bar;
CommandLineOptions opts = CommandLineOptions("test")
.MandatoryOption("--foo", "", &foo)
.MandatoryOption("--bar", "", &bar);
@@ -92,13 +90,14 @@
std::ostream fakeStdErr(nullptr);
bool success = opts.Parse({"--foo", "FOO", "--foo", "BAR"}, fakeStdErr);
ASSERT_TRUE(success);
- ASSERT_EQ(args.size(), 2u);
+ ASSERT_EQ(args.size(), 2U);
ASSERT_EQ(args[0], "FOO");
ASSERT_EQ(args[1], "BAR");
}
TEST(CommandLineOptionsTests, OptionalOption) {
- std::string foo, bar;
+ std::string foo;
+ std::string bar;
CommandLineOptions opts = CommandLineOptions("test")
.OptionalOption("--foo", "", &foo)
.OptionalOption("--bar", "", &bar);
@@ -123,7 +122,8 @@
}
TEST(CommandLineOptionsTests, CornerCases) {
- std::string foo, bar;
+ std::string foo;
+ std::string bar;
bool baz = false;
CommandLineOptions opts = CommandLineOptions("test")
.MandatoryOption("--foo", "", &foo)
@@ -150,7 +150,7 @@
nullptr,
};
std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(3, argv);
- ASSERT_EQ(v->size(), 2ul);
+ ASSERT_EQ(v->size(), 2UL);
ASSERT_EQ((*v)[0], "--foo");
ASSERT_EQ((*v)[1], "FOO");
}
@@ -161,12 +161,16 @@
nullptr,
};
std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(1, argv);
- ASSERT_EQ(v->size(), 0ul);
+ ASSERT_EQ(v->size(), 0UL);
}
TEST(CommandLineOptionsTests, Usage) {
- std::string arg1, arg2, arg3, arg4;
- bool arg5 = false, arg6 = false;
+ std::string arg1;
+ std::string arg2;
+ std::string arg3;
+ std::string arg4;
+ bool arg5 = false;
+ bool arg6 = false;
std::vector<std::string> arg7;
CommandLineOptions opts = CommandLineOptions("test")
.MandatoryOption("--aa", "description-aa", &arg1)
@@ -188,5 +192,4 @@
std::string::npos);
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 0c6439a..4bf832a 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -29,9 +29,7 @@
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
-namespace utils {
+namespace android::idmap2::utils {
TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
const auto& root = GetTestDataPath();
@@ -39,7 +37,7 @@
[](unsigned char type ATTRIBUTE_UNUSED,
const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 4u);
+ ASSERT_EQ(v->size(), 4U);
ASSERT_EQ(
std::set<std::string>(v->begin(), v->end()),
std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"}));
@@ -48,10 +46,10 @@
TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
const auto& root = GetTestDataPath();
auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
- return type == DT_REG && path.size() > 4 && !path.compare(path.size() - 4, 4, ".apk");
+ return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
});
ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 4u);
+ ASSERT_EQ(v->size(), 4U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
root + "/overlay/overlay-static-1.apk",
@@ -71,6 +69,4 @@
close(pipefd[0]);
}
-} // namespace utils
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 5c4e857..22f48e9 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -46,20 +46,21 @@
using ::android::util::ExecuteBinary;
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
class Idmap2BinaryTests : public Idmap2Tests {};
-static void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
- const std::string& overlay_apk_path) {
+namespace {
+
+void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
+ const std::string& overlay_apk_path) {
// check that the idmap file looks reasonable (IdmapTests is responsible for
// more in-depth verification)
ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
- ASSERT_EQ(idmap.GetData().size(), 1u);
+ ASSERT_EQ(idmap.GetData().size(), 1U);
}
#define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path) \
@@ -67,6 +68,8 @@
ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
} while (0)
+} // namespace
+
TEST_F(Idmap2BinaryTests, Create) {
// clang-format off
auto result = ExecuteBinary({"idmap2",
@@ -309,5 +312,4 @@
ASSERT_NE(result->status, EXIT_SUCCESS);
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0379aa4..963f22e 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -37,8 +37,7 @@
using ::testing::IsNull;
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
TEST(IdmapTests, TestCanonicalIdmapPathFor) {
ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
@@ -50,10 +49,10 @@
std::istringstream stream(raw);
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_EQ(header->GetMagic(), 0x504d4449u);
- ASSERT_EQ(header->GetVersion(), 0x01u);
- ASSERT_EQ(header->GetTargetCrc(), 0x1234u);
- ASSERT_EQ(header->GetOverlayCrc(), 0x5678u);
+ ASSERT_EQ(header->GetMagic(), 0x504d4449U);
+ ASSERT_EQ(header->GetVersion(), 0x01U);
+ ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
+ ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
}
@@ -77,8 +76,8 @@
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_EQ(header->GetTargetPackageId(), 0x7fu);
- ASSERT_EQ(header->GetTypeCount(), 2u);
+ ASSERT_EQ(header->GetTargetPackageId(), 0x7fU);
+ ASSERT_EQ(header->GetTypeCount(), 2U);
}
TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
@@ -89,11 +88,11 @@
std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetTargetTypeId(), 0x02u);
- ASSERT_EQ(data->GetOverlayTypeId(), 0x02u);
- ASSERT_EQ(data->GetEntryCount(), 1u);
- ASSERT_EQ(data->GetEntryOffset(), 0u);
- ASSERT_EQ(data->GetEntry(0), 0u);
+ ASSERT_EQ(data->GetTargetTypeId(), 0x02U);
+ ASSERT_EQ(data->GetOverlayTypeId(), 0x02U);
+ ASSERT_EQ(data->GetEntryCount(), 1U);
+ ASSERT_EQ(data->GetEntryOffset(), 0U);
+ ASSERT_EQ(data->GetEntry(0), 0U);
}
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
@@ -104,24 +103,24 @@
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2u);
+ ASSERT_EQ(types.size(), 2U);
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
- ASSERT_EQ(types[0]->GetEntryCount(), 1u);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1U);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
- ASSERT_EQ(types[1]->GetEntryCount(), 3u);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
+ ASSERT_EQ(types[1]->GetEntryCount(), 3U);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
}
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
@@ -133,35 +132,35 @@
ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
- ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
- ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234u);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678u);
+ ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1u);
+ ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2u);
+ ASSERT_EQ(types.size(), 2U);
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
- ASSERT_EQ(types[0]->GetEntryCount(), 1u);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1U);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
- ASSERT_EQ(types[1]->GetEntryCount(), 3u);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
+ ASSERT_EQ(types[1]->GetEntryCount(), 3U);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
}
TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
@@ -189,8 +188,8 @@
ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
- ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+ ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
@@ -198,30 +197,30 @@
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1u);
+ ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+ ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
+ ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2u);
+ ASSERT_EQ(types.size(), 2U);
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01u);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01u);
- ASSERT_EQ(types[0]->GetEntryCount(), 1u);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
+ ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
+ ASSERT_EQ(types[0]->GetEntryCount(), 1U);
+ ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
+ ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02u);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02u);
- ASSERT_EQ(types[1]->GetEntryCount(), 4u);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+ ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
+ ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
+ ASSERT_EQ(types[1]->GetEntryCount(), 4U);
+ ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
+ ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
- ASSERT_EQ(types[1]->GetEntry(3), 0x0002u);
+ ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+ ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
}
TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
@@ -346,23 +345,23 @@
explicit TestVisitor(std::ostream& stream) : stream_(stream) {
}
- void visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+ void visit(const Idmap& idmap ATTRIBUTE_UNUSED) override {
stream_ << "TestVisitor::visit(Idmap)" << std::endl;
}
- void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) {
+ void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) override {
stream_ << "TestVisitor::visit(IdmapHeader)" << std::endl;
}
- void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) {
+ void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) override {
stream_ << "TestVisitor::visit(IdmapData)" << std::endl;
}
- void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) {
+ void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) override {
stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
}
- void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) {
+ void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) override {
stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
}
@@ -391,5 +390,4 @@
"TestVisitor::visit(IdmapData::TypeEntry)\n");
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/Main.cpp b/cmds/idmap2/tests/Main.cpp
index f2469ea..2b13fed 100644
--- a/cmds/idmap2/tests/Main.cpp
+++ b/cmds/idmap2/tests/Main.cpp
@@ -22,15 +22,13 @@
#include "TestHelpers.h"
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
-const std::string GetTestDataPath() {
+std::string GetTestDataPath() {
return base::GetExecutableDirectory() + "/tests/data";
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index da97792..7736bc0 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -29,13 +29,11 @@
#include "TestHelpers.h"
-using ::testing::IsNull;
using ::testing::NotNull;
using android::ApkAssets;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
@@ -79,5 +77,4 @@
ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos);
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index c28ce2e..0318cd2 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -27,11 +27,9 @@
#include "TestHelpers.h"
-using ::testing::IsNull;
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
@@ -80,5 +78,4 @@
ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f020000 -> 0x7f020000\n"), std::string::npos);
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 7f60d75..ad78685 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -28,8 +28,7 @@
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
class ResourceUtilsTests : public Idmap2Tests {
protected:
@@ -52,15 +51,14 @@
};
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
- Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U);
ASSERT_TRUE(name);
ASSERT_EQ(*name, "integer/int1");
}
TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
- Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456U);
ASSERT_FALSE(name);
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index 18dc541..45525a5 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -19,8 +19,7 @@
#include <string>
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
const unsigned char idmap_raw_data[] = {
// IDMAP HEADER
@@ -117,7 +116,7 @@
const unsigned int idmap_raw_data_len = 565;
-const std::string GetTestDataPath();
+std::string GetTestDataPath();
class Idmap2Tests : public testing::Test {
protected:
@@ -162,7 +161,6 @@
std::string idmap_path_;
};
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
#endif // IDMAP2_TESTS_TESTHELPERS_H_
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
index 97ff03e..fe79d8f 100644
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ b/cmds/idmap2/tests/XmlTests.cpp
@@ -27,8 +27,7 @@
using ::testing::IsNull;
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
TEST(XmlTests, Create) {
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
@@ -58,15 +57,14 @@
auto attrs = xml->FindTag("c");
ASSERT_THAT(attrs, NotNull());
- ASSERT_EQ(attrs->size(), 4u);
+ ASSERT_EQ(attrs->size(), 4U);
ASSERT_EQ(attrs->at("type_string"), "fortytwo");
ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
- ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0u);
+ ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U);
auto fail = xml->FindTag("does-not-exist");
ASSERT_THAT(fail, IsNull());
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
index 6e4a501..79be43c 100644
--- a/cmds/idmap2/tests/ZipFileTests.cpp
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -28,8 +28,7 @@
using ::testing::IsNull;
using ::testing::NotNull;
-namespace android {
-namespace idmap2 {
+namespace android::idmap2 {
TEST(ZipFileTests, BasicOpen) {
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
@@ -66,5 +65,4 @@
ASSERT_THAT(fail, IsNull());
}
-} // namespace idmap2
-} // namespace android
+} // namespace android::idmap2
diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h
index 784c181..a6074e7 100644
--- a/cmds/incident_helper/src/TextParserBase.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -30,7 +30,7 @@
public:
String8 name;
- TextParserBase(String8 name) : name(name) {};
+ explicit TextParserBase(String8 name) : name(name) {};
virtual ~TextParserBase() {};
virtual status_t Parse(const int in, const int out) const = 0;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index c02a349..09dc8e6 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -109,7 +109,7 @@
class Reader
{
public:
- Reader(const int fd);
+ explicit Reader(const int fd);
~Reader();
bool readLine(std::string* line);
@@ -162,7 +162,7 @@
class Message
{
public:
- Message(Table* table);
+ explicit Message(Table* table);
~Message();
// Reconstructs the typical proto message by adding its message fields.
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index 6252ad2..c63a183 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -97,7 +97,7 @@
// ================================================================================
class IncidentService : public BnIncidentManager {
public:
- IncidentService(const sp<Looper>& handlerLooper);
+ explicit IncidentService(const sp<Looper>& handlerLooper);
virtual ~IncidentService();
virtual Status reportIncident(const IncidentReportArgs& args);
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index a3df490..a0159d9 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -83,7 +83,7 @@
static PrivacySpec new_spec(int dest);
private:
- PrivacySpec(uint8_t dest) : dest(dest) {}
+ explicit PrivacySpec(uint8_t dest) : dest(dest) {}
};
} // namespace incidentd
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 45fd944..2a3abd7 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -89,7 +89,7 @@
ReportRequestSet batch;
Reporter(); // PROD must use this constructor.
- Reporter(const char* directory); // For testing purpose only.
+ explicit Reporter(const char* directory); // For testing purpose only.
virtual ~Reporter();
// Run the report as described in the batch and args parameters.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 10d2268..32ec1ba 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -415,7 +415,7 @@
bool workerDone;
status_t workerError;
- WorkerThreadData(const WorkerThreadSection* section);
+ explicit WorkerThreadData(const WorkerThreadSection* section);
virtual ~WorkerThreadData();
};
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 0114ff4..f0b751d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -216,6 +216,7 @@
"tests/anomaly/AnomalyTracker_test.cpp",
"tests/ConfigManager_test.cpp",
"tests/external/puller_util_test.cpp",
+ "tests/external/StatsPuller_test.cpp",
"tests/indexed_priority_queue_test.cpp",
"tests/LogEntryMatcher_test.cpp",
"tests/LogEvent_test.cpp",
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a981997..69cb264 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -293,7 +293,7 @@
// Then, check stats-data directory to see there's any file containing
// ConfigMetricsReport from previous shutdowns to concatenate to reports.
- StorageManager::appendConfigMetricsReport(key, proto);
+ StorageManager::appendConfigMetricsReport(key, proto, erase_data);
auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 04173b2..f2a4663 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -296,6 +296,7 @@
ADB_DUMP, &proto);
proto.end(reportsListToken);
proto.flush(out);
+ proto.clear();
}
}
@@ -466,23 +467,12 @@
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
- // If it's a userdebug or eng build, then the shell user can
- // impersonate other uids.
- if (mEngBuild) {
- const char* s = args[1].c_str();
- if (*s != '\0') {
- char* end = NULL;
- uid = strtol(s, &end, 0);
- if (*end == '\0') {
- name.assign(args[2].c_str(), args[2].size());
- good = true;
- }
- }
- } else {
- dprintf(out,
- "The metrics can only be dumped for other UIDs on eng or userdebug "
- "builds.\n");
+ good = getUidFromArgs(args, 1, uid);
+ if (!good) {
+ dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+ "other UIDs on eng or userdebug builds.\n");
}
+ name.assign(args[2].c_str(), args[2].size());
}
if (!good) {
print_cmd_help(out);
@@ -518,23 +508,12 @@
name.assign(args[2].c_str(), args[2].size());
good = true;
} else if (argCount == 4) {
- // If it's a userdebug or eng build, then the shell user can
- // impersonate other uids.
- if (mEngBuild) {
- const char* s = args[2].c_str();
- if (*s != '\0') {
- char* end = NULL;
- uid = strtol(s, &end, 0);
- if (*end == '\0') {
- name.assign(args[3].c_str(), args[3].size());
- good = true;
- }
- }
- } else {
- dprintf(err,
- "The config can only be set for other UIDs on eng or userdebug "
- "builds.\n");
+ good = getUidFromArgs(args, 2, uid);
+ if (!good) {
+ dprintf(err, "Invalid UID. Note that the config can only be set for "
+ "other UIDs on eng or userdebug builds.\n");
}
+ name.assign(args[3].c_str(), args[3].size());
} else if (argCount == 2 && args[1] == "remove") {
good = true;
}
@@ -612,23 +591,12 @@
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
- // If it's a userdebug or eng build, then the shell user can
- // impersonate other uids.
- if (mEngBuild) {
- const char* s = args[1].c_str();
- if (*s != '\0') {
- char* end = NULL;
- uid = strtol(s, &end, 0);
- if (*end == '\0') {
- name.assign(args[2].c_str(), args[2].size());
- good = true;
- }
- }
- } else {
- dprintf(out,
- "The metrics can only be dumped for other UIDs on eng or userdebug "
- "builds.\n");
+ good = getUidFromArgs(args, 1, uid);
+ if (!good) {
+ dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+ "other UIDs on eng or userdebug builds.\n");
}
+ name.assign(args[2].c_str(), args[2].size());
}
if (good) {
vector<uint8_t> data;
@@ -714,18 +682,14 @@
state = atoi(args[2].c_str());
good = true;
} else if (argCount == 4) {
- uid = atoi(args[1].c_str());
- // If it's a userdebug or eng build, then the shell user can impersonate other uids.
- // Otherwise, the uid must match the actual caller's uid.
- if (mEngBuild || (uid >= 0 && (uid_t)uid == IPCThreadState::self()->getCallingUid())) {
- label = atoi(args[2].c_str());
- state = atoi(args[3].c_str());
- good = true;
- } else {
+ good = getUidFromArgs(args, 1, uid);
+ if (!good) {
dprintf(out,
- "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
- "on eng or userdebug builds.\n");
+ "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be "
+ "done for other UIDs on eng or userdebug builds.\n");
}
+ label = atoi(args[2].c_str());
+ state = atoi(args[3].c_str());
}
if (good) {
dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
@@ -740,7 +704,7 @@
status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
- if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) {
+ if (mPullerManager->Pull(s, &stats)) {
for (const auto& it : stats) {
dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
@@ -792,6 +756,28 @@
}
}
+bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
+ const char* s = args[uidArgIndex].c_str();
+ if (*s == '\0') {
+ return false;
+ }
+ char* endc = NULL;
+ int64_t longUid = strtol(s, &endc, 0);
+ if (*endc != '\0') {
+ return false;
+ }
+ int32_t goodUid = static_cast<int32_t>(longUid);
+ if (longUid < 0 || static_cast<uint64_t>(longUid) != static_cast<uid_t>(goodUid)) {
+ return false; // It was not of uid_t type.
+ }
+ uid = goodUid;
+
+ int32_t callingUid = IPCThreadState::self()->getCallingUid();
+ return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
+ || (callingUid == goodUid) // Anyone can 'impersonate' themselves.
+ || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
const vector<String16>& version_string,
const vector<String16>& app,
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index cd4d601..135a3c9 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -291,6 +291,15 @@
status_t cmd_print_logs(int outFd, const Vector<String8>& args);
/**
+ * Writes the value of args[uidArgIndex] into uid.
+ * Returns whether the uid is reasonable (type uid_t) and whether
+ * 1. it is equal to the calling uid, or
+ * 2. the device is mEngBuild, or
+ * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
+ */
+ bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid);
+
+ /**
* Adds a configuration after checking permissions and obtaining UID from binder call.
*/
bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
@@ -340,6 +349,7 @@
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
+ FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 3badb1f..bca858e 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -42,7 +42,7 @@
* Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
*/
struct InternalAlarm : public RefBase {
- InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
+ explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
}
const uint32_t timestampSec;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0c05be1..e541543 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -172,6 +172,7 @@
WifiEnabledStateChanged wifi_enabled_state_changed = 113;
WifiRunningStateChanged wifi_running_state_changed = 114;
AppCompacted app_compacted = 115;
+ NetworkDnsEventReported network_dns_event_Reported = 116;
}
// Pulled events will start at field 10000.
@@ -926,7 +927,7 @@
* Logs wifi signal strength changes.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
*/
message WifiSignalStrengthChanged {
// Signal strength, from frameworks/base/core/proto/android/telephony/enums.proto.
@@ -937,7 +938,7 @@
* Logs wifi scans performed by an app.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
*/
message WifiScanStateChanged {
repeated AttributionNode attribution_node = 1;
@@ -953,7 +954,7 @@
* Logs wifi multicast locks held by an app
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMulticastLockManager.java
*/
message WifiMulticastLockStateChanged {
repeated AttributionNode attribution_node = 1;
@@ -963,6 +964,8 @@
ON = 1;
}
optional State state = 2;
+
+ optional string tag = 3;
}
/**
@@ -3473,10 +3476,10 @@
* Pulls on-device BatteryStats power use calculations for the overall device.
*/
message DeviceCalculatedPowerUse {
- // Power used by the device in mAh, as computed by BatteryStats, since BatteryStats last reset
- // (i.e. roughly since device was last significantly charged).
- // Currently, this is BatteryStatsHelper.getComputedPower() (not getTotalPower()).
- optional float computed_power_milli_amp_hours = 1;
+ // Power used by the device in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since
+ // BatteryStats last reset (i.e. roughly since device was last significantly charged).
+ // Currently, this is from BatteryStatsHelper.getComputedPower() (not getTotalPower()).
+ optional int64 computed_power_nano_amp_secs = 1;
}
/**
@@ -3488,9 +3491,9 @@
// Uid being blamed. Note: isolated uids have already been mapped to host uid.
optional int32 uid = 1 [(is_uid) = true];
- // Power used by this uid in mAh, as computed by BatteryStats, since BatteryStats last reset
- // (i.e. roughly since device was last significantly charged).
- optional float power_milli_amp_hours = 2;
+ // Power used by this uid in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since
+ // BatteryStats last reset (i.e. roughly since device was last significantly charged).
+ optional int64 power_nano_amp_secs = 2;
}
/**
@@ -3524,9 +3527,9 @@
}
optional DrainType drain_type = 1;
- // Power used by this item in mAh, as computed by BatteryStats, since BatteryStats last reset
- // (i.e. roughly since device was last significantly charged).
- optional float power_milli_amp_hours = 2;
+ // Power used by this item in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since
+ // BatteryStats last reset (i.e. roughly since device was last significantly charged).
+ optional int64 power_nano_amp_secs = 2;
}
/**
@@ -3670,7 +3673,7 @@
SOME = 1;
FULL = 2;
}
- optional Action action = 3 [default = UNKNOWN];
+ optional Action action = 3;
// Total RSS in kilobytes consumed by the process prior to compaction.
optional int64 before_rss_total_kilobytes = 4;
@@ -3702,13 +3705,61 @@
// The last compaction action performed for this app.
optional Action last_action = 13;
- // The last time that compaction was attempted on this process in seconds
- // since boot.
- optional int64 last_compact_timestamp = 14;
+ // The last time that compaction was attempted on this process in milliseconds
+ // since boot, not including sleep (see SystemClock.uptimeMillis()).
+ optional int64 last_compact_timestamp_ms_since_boot = 14;
- // The oom_adj at the time of compaction.
- optional int32 oom_adj = 15;
+ // The oom_score_adj at the time of compaction.
+ optional int32 oom_score_adj = 15;
// The process state at the time of compaction.
optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN];
}
+
+/**
+ * Logs the latency period(in microseconds) and the return code of
+ * the DNS(Domain Name System) lookups.
+ * These 4 methods(GETADDRINFO,GETHOSTBYNAME,GETHOSTBYADDR,RES_NSEND)
+ * to get info(address or hostname) from DNS server(or DNS cache).
+ * Logged from:
+ * /system/netd/server/DnsProxyListener.cpp
+ */
+message NetworkDnsEventReported {
+ // The types of the DNS lookups, as defined in
+ //system/netd/server/binder/android/net/metrics/INetdEventListener.aidl
+ enum EventType {
+ EVENT_UNKNOWN = 0;
+ EVENT_GETADDRINFO = 1;
+ EVENT_GETHOSTBYNAME = 2;
+ EVENT_GETHOSTBYADDR = 3;
+ EVENT_RES_NSEND = 4;
+ }
+ optional EventType event_type = 1;
+
+ // The return value of the DNS resolver for each DNS lookups.
+ //bionic/libc/include/netdb.h
+ //system/netd/resolv/include/netd_resolv/resolv.h
+ enum ReturnCode {
+ EAI_NOERR = 0;
+ EAI_ADDRFAMILY = 1;
+ EAI_AGAIN = 2;
+ EAI_BADFLAGS = 3;
+ EAI_FAIL = 4;
+ EAI_FAMILY = 5;
+ EAI_MEMORY = 6;
+ EAI_NODATA = 7;
+ EAI_NONAME = 8;
+ EAI_SERVICE = 9;
+ EAI_SOCKTYPE = 10;
+ EAI_SYSTEM = 11;
+ EAI_BADHINTS = 12;
+ EAI_PROTOCOL = 13;
+ EAI_OVERFLOW = 14;
+ RESOLV_TIMEOUT = 255;
+ EAI_MAX = 256;
+ }
+ optional ReturnCode return_code = 2;
+
+ // The latency period(in microseconds) it took for this DNS lookup to complete.
+ optional int32 latency_micros = 3;
+}
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index a6f88af..2c88147 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -29,7 +29,7 @@
class ConditionWizard : public virtual android::RefBase {
public:
ConditionWizard(){}; // for testing
- ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
+ explicit ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
: mAllConditions(conditionTrackers){};
virtual ~ConditionWizard(){};
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
index dc79519..4cc9393 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -33,7 +33,7 @@
class ConfigKey {
public:
ConfigKey();
- explicit ConfigKey(const ConfigKey& that);
+ ConfigKey(const ConfigKey& that);
ConfigKey(int uid, const int64_t& id);
~ConfigKey();
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/cmds/statsd/src/external/PowerStatsPuller.h
index dd5ff8f..6f15bd6 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.h
+++ b/cmds/statsd/src/external/PowerStatsPuller.h
@@ -28,6 +28,8 @@
class PowerStatsPuller : public StatsPuller {
public:
PowerStatsPuller();
+
+private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
};
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
index 9b238eaf5..f650fcc 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.h
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -28,7 +28,9 @@
*/
class ResourceHealthManagerPuller : public StatsPuller {
public:
- ResourceHealthManagerPuller(int tagId);
+ explicit ResourceHealthManagerPuller(int tagId);
+
+private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
};
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.h b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
index 13c675a..5313792 100644
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.h
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.h
@@ -29,6 +29,8 @@
class ResourceThermalManagerPuller : public StatsPuller {
public:
ResourceThermalManagerPuller();
+
+private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
};
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.h b/cmds/statsd/src/external/StatsCompanionServicePuller.h
index 0a49732..2e13320 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.h
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.h
@@ -25,14 +25,14 @@
class StatsCompanionServicePuller : public StatsPuller {
public:
- StatsCompanionServicePuller(int tagId);
- bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
+ explicit StatsCompanionServicePuller(int tagId);
void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) override;
private:
Mutex mStatsCompanionServiceLock;
sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 7043d66..c7c22ee 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -34,48 +34,52 @@
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
- // Pullers can cause significant impact to system health and battery.
- // So that we don't pull too frequently.
- mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
- VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
}
-bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
lock_guard<std::mutex> lock(mLock);
- int64_t wallClockTimeNs = getWallClockNs();
+ int64_t elapsedTimeNs = getElapsedRealtimeNs();
StatsdStats::getInstance().notePull(mTagId);
- if (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs) {
- (*data) = mCachedData;
- StatsdStats::getInstance().notePullFromCache(mTagId);
- StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
- return true;
+ const bool shouldUseCache = elapsedTimeNs - mLastPullTimeNs <
+ StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs;
+ if (shouldUseCache) {
+ if (mHasGoodData) {
+ (*data) = mCachedData;
+ StatsdStats::getInstance().notePullFromCache(mTagId);
+ }
+ return mHasGoodData;
}
- if (mMinPullIntervalNs > elapsedTimeNs - mLastPullTimeNs) {
- mMinPullIntervalNs = elapsedTimeNs - mLastPullTimeNs;
- StatsdStats::getInstance().updateMinPullIntervalSec(mTagId,
- mMinPullIntervalNs / NS_PER_SEC);
+
+ if (mLastPullTimeNs > 0) {
+ StatsdStats::getInstance().updateMinPullIntervalSec(
+ mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
}
mCachedData.clear();
mLastPullTimeNs = elapsedTimeNs;
- int64_t pullStartTimeNs = getElapsedRealtimeNs();
- bool ret = PullInternal(&mCachedData);
- if (!ret) {
- mCachedData.clear();
- return false;
+ mHasGoodData = PullInternal(&mCachedData);
+ if (!mHasGoodData) {
+ return mHasGoodData;
}
- StatsdStats::getInstance().notePullTime(mTagId, getElapsedRealtimeNs() - pullStartTimeNs);
- for (const shared_ptr<LogEvent>& data : mCachedData) {
- data->setElapsedTimestampNs(elapsedTimeNs);
- data->setLogdWallClockTimestampNs(wallClockTimeNs);
+ const int64_t pullDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
+ StatsdStats::getInstance().notePullTime(mTagId, pullDurationNs);
+ const bool pullTimeOut =
+ pullDurationNs > StatsPullerManager::kAllPullAtomInfo.at(mTagId).pullTimeoutNs;
+ if (pullTimeOut) {
+ // Something went wrong. Discard the data.
+ clearCacheLocked();
+ mHasGoodData = false;
+ StatsdStats::getInstance().notePullTimeout(mTagId);
+ ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
+ (long long)pullDurationNs);
+ return mHasGoodData;
}
if (mCachedData.size() > 0) {
mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
- (*data) = mCachedData;
}
- StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
- return ret;
+ (*data) = mCachedData;
+ return mHasGoodData;
}
int StatsPuller::ForceClearCache() {
@@ -84,6 +88,10 @@
int StatsPuller::clearCache() {
lock_guard<std::mutex> lock(mLock);
+ return clearCacheLocked();
+}
+
+int StatsPuller::clearCacheLocked() {
int ret = mCachedData.size();
mCachedData.clear();
mLastPullTimeNs = 0;
@@ -91,7 +99,8 @@
}
int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
- if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
+ if (timestampNs - mLastPullTimeNs >
+ StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs) {
return clearCache();
} else {
return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index cafd797..c83c4f8 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -18,7 +18,6 @@
#include <android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
-#include <utils/String16.h>
#include <mutex>
#include <vector>
#include "packages/UidMap.h"
@@ -33,14 +32,20 @@
class StatsPuller : public virtual RefBase {
public:
- StatsPuller(const int tagId);
+ explicit StatsPuller(const int tagId);
virtual ~StatsPuller() {}
- // Pulls the data. The returned data will have elapsedTimeNs set as timeNs
- // and will have wallClockTimeNs set as current wall clock time.
- // Return true if the pull is successful.
- bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
+ // Pulls the most recent data.
+ // The data may be served from cache if consecutive pulls come within
+ // predefined cooldown time.
+ // Returns true if the pull was successful.
+ // Returns false when
+ // 1) the pull fails
+ // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+ // If a metric wants to make any change to the data, like timestamps, it
+ // should make a copy as this data may be shared with multiple metrics.
+ bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
@@ -53,29 +58,30 @@
virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
protected:
- // The atom tag id this puller pulls
const int mTagId;
private:
mutable std::mutex mLock;
- // Minimum time before this puller does actual pull again.
- // If a pull request comes before cooldown, a cached version from purevious pull
- // will be returned.
- // The actual value should be determined by individual pullers.
- int64_t mCoolDownNs;
- // For puller stats
- int64_t mMinPullIntervalNs = LONG_MAX;
+ // Real puller impl.
virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
- // Cache of data from last pull. If next request comes before cool down finishes,
- // cached data will be returned.
- std::vector<std::shared_ptr<LogEvent>> mCachedData;
+ bool mHasGoodData = false;
int64_t mLastPullTimeNs;
+ // Cache of data from last pull. If next request comes before cool down finishes,
+ // cached data will be returned.
+ // Cached data is cleared when
+ // 1) A pull fails
+ // 2) A new pull request comes after cooldown time.
+ // 3) clearCache is called.
+ std::vector<std::shared_ptr<LogEvent>> mCachedData;
+
int clearCache();
+ int clearCacheLocked();
+
static sp<UidMap> mUidMap;
};
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index c070ca3..4a716cf 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -53,195 +53,172 @@
const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// wifi_bytes_transfer
{android::util::WIFI_BYTES_TRANSFER,
- {{2, 3, 4, 5},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+ {.additiveFields = {2, 3, 4, 5},
+ .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
// wifi_bytes_transfer_by_fg_bg
{android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- {{3, 4, 5, 6},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+ {.additiveFields = {3, 4, 5, 6},
+ .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
// mobile_bytes_transfer
{android::util::MOBILE_BYTES_TRANSFER,
- {{2, 3, 4, 5},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+ {.additiveFields = {2, 3, 4, 5},
+ .puller = new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
// mobile_bytes_transfer_by_fg_bg
{android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- {{3, 4, 5, 6},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+ {.additiveFields = {3, 4, 5, 6},
+ .puller =
+ new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
// bluetooth_bytes_transfer
{android::util::BLUETOOTH_BYTES_TRANSFER,
- {{2, 3},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+ {.additiveFields = {2, 3},
+ .puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
// kernel_wakelock
{android::util::KERNEL_WAKELOCK,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ {.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
// subsystem_sleep_state
- {android::util::SUBSYSTEM_SLEEP_STATE,
- {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+ {android::util::SUBSYSTEM_SLEEP_STATE, {.puller = new SubsystemSleepStatePuller()}},
// on_device_power_measurement
- {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
+ {android::util::ON_DEVICE_POWER_MEASUREMENT, {.puller = new PowerStatsPuller()}},
// cpu_time_per_freq
{android::util::CPU_TIME_PER_FREQ,
- {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ {.additiveFields = {3},
+ .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
// cpu_time_per_uid
{android::util::CPU_TIME_PER_UID,
- {{2, 3},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
+ {.additiveFields = {2, 3},
+ .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
// cpu_time_per_uid_freq
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_TIME_PER_UID_FREQ,
- {{4},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
+ {.additiveFields = {4},
+ .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
// cpu_active_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_ACTIVE_TIME,
- {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+ {.additiveFields = {2},
+ .puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
// cpu_cluster_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_CLUSTER_TIME,
- {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+ {.additiveFields = {3},
+ .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
// wifi_activity_energy_info
{android::util::WIFI_ACTIVITY_INFO,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+ {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
// modem_activity_info
{android::util::MODEM_ACTIVITY_INFO,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
// bluetooth_activity_info
{android::util::BLUETOOTH_ACTIVITY_INFO,
- {{},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+ {.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
// system_elapsed_realtime
{android::util::SYSTEM_ELAPSED_REALTIME,
- {{},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ {.pullTimeoutNs = NS_PER_SEC / 2,
+ .coolDownNs = NS_PER_SEC,
+ .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
// system_uptime
{android::util::SYSTEM_UPTIME,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
// remaining_battery_capacity
{android::util::REMAINING_BATTERY_CAPACITY,
- {{},
- 1 * NS_PER_SEC,
- new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+ {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
// full_battery_capacity
{android::util::FULL_BATTERY_CAPACITY,
- {{},
- 1 * NS_PER_SEC,
- new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+ {.puller = new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// battery_voltage
{android::util::BATTERY_VOLTAGE,
- {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
- // battery_level
+ {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+ // battery_voltage
{android::util::BATTERY_LEVEL,
- {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+ {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
- {{4, 5, 6, 7, 8, 9},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+ {.additiveFields = {4, 5, 6, 7, 8, 9},
+ .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
// native_process_memory_state
{android::util::NATIVE_PROCESS_MEMORY_STATE,
- {{3, 4, 5, 6},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
+ {.additiveFields = {3, 4, 5, 6},
+ .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
{android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
- {{3},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
+ {.additiveFields = {3},
+ .puller =
+ new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
// temperature
- {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
+ {android::util::TEMPERATURE, {.puller = new ResourceThermalManagerPuller()}},
// binder_calls
{android::util::BINDER_CALLS,
- {{4, 5, 6, 8, 12},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
+ {.additiveFields = {4, 5, 6, 8, 12},
+ .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
// binder_calls_exceptions
{android::util::BINDER_CALLS_EXCEPTIONS,
- {{},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
+ {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
// looper_stats
{android::util::LOOPER_STATS,
- {{5, 6, 7, 8, 9},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
+ {.additiveFields = {5, 6, 7, 8, 9},
+ .puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
// Disk Stats
{android::util::DISK_STATS,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+ {.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}},
// Directory usage
{android::util::DIRECTORY_USAGE,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+ {.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
// Size of app's code, data, and cache
{android::util::APP_SIZE,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+ {.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}},
// Size of specific categories of files. Eg. Music.
{android::util::CATEGORY_SIZE,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+ {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
// Number of fingerprints registered to each user.
{android::util::NUM_FINGERPRINTS,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
+ {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
// ProcStats.
{android::util::PROC_STATS,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+ {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}},
// ProcStatsPkgProc.
{android::util::PROC_STATS_PKG_PROC,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+ {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
// Disk I/O stats per uid.
{android::util::DISK_IO,
- {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
- 3 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DISK_IO)}},
+ {.additiveFields = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
+ .coolDownNs = 3 * NS_PER_SEC,
+ .puller = new StatsCompanionServicePuller(android::util::DISK_IO)}},
// PowerProfile constants for power model calculations.
{android::util::POWER_PROFILE,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+ {.puller = new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
// Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
{android::util::PROCESS_CPU_TIME,
- {{} /* additive fields */,
- 5 * NS_PER_SEC /* min cool-down in seconds*/,
- new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+ {.coolDownNs = 5 * NS_PER_SEC /* min cool-down in seconds*/,
+ .puller = new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
{android::util::CPU_TIME_PER_THREAD_FREQ,
- {{7, 9, 11, 13, 15, 17, 19, 21},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+ {.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
+ .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
// DeviceCalculatedPowerUse.
{android::util::DEVICE_CALCULATED_POWER_USE,
- {{},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+ {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
// DeviceCalculatedPowerBlameUid.
{android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
- {{}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+ {.puller = new StatsCompanionServicePuller(
+ android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
// DeviceCalculatedPowerBlameOther.
{android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
- {{},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+ {.puller = new StatsCompanionServicePuller(
+ android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
// BuildInformation.
{android::util::BUILD_INFORMATION,
- {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
+ {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
}
-bool StatsPullerManager::Pull(const int tagId, const int64_t timeNs,
- vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
- bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(timeNs, data);
+ bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
VLOG("pulled %d items", (int)data->size());
return ret;
} else {
@@ -333,8 +310,9 @@
}
}
-void StatsPullerManager::OnAlarmFired(const int64_t currentTimeNs) {
+void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
AutoMutex _l(mLock);
+ int64_t wallClockNs = getWallClockNs();
int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
@@ -344,7 +322,7 @@
vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
if (pair.second.size() != 0) {
for (ReceiverInfo& receiverInfo : pair.second) {
- if (receiverInfo.nextPullTimeNs <= currentTimeNs) {
+ if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
receivers.push_back(&receiverInfo);
} else {
if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
@@ -360,22 +338,38 @@
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- if (Pull(pullInfo.first, currentTimeNs, &data)) {
- for (const auto& receiverInfo : pullInfo.second) {
- sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
- if (receiverPtr != nullptr) {
- receiverPtr->onDataPulled(data);
- // we may have just come out of a coma, compute next pull time
- receiverInfo->nextPullTimeNs =
- (currentTimeNs - receiverInfo->nextPullTimeNs) /
- receiverInfo->intervalNs * receiverInfo->intervalNs +
- receiverInfo->intervalNs + receiverInfo->nextPullTimeNs;
- if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
- minNextPullTimeNs = receiverInfo->nextPullTimeNs;
- }
- } else {
- VLOG("receiver already gone.");
+ if (!Pull(pullInfo.first, &data)) {
+ VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
+ continue;
+ }
+ StatsdStats::getInstance().notePullDelay(pullInfo.first,
+ getElapsedRealtimeNs() - elapsedTimeNs);
+
+ // Convention is to mark pull atom timestamp at request time.
+ // If we pull at t0, puller starts at t1, finishes at t2, and send back
+ // at t3, we mark t0 as its timestamp, which should correspond to its
+ // triggering event, such as condition change at t0.
+ // Here the triggering event is alarm fired from AlarmManager.
+ // In ValueMetricProducer and GaugeMetricProducer we do same thing
+ // when pull on condition change, etc.
+ for (auto& event : data) {
+ event->setElapsedTimestampNs(elapsedTimeNs);
+ event->setLogdWallClockTimestampNs(wallClockNs);
+ }
+
+ for (const auto& receiverInfo : pullInfo.second) {
+ sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
+ if (receiverPtr != nullptr) {
+ receiverPtr->onDataPulled(data);
+ // we may have just come out of a coma, compute next pull time
+ int numBucketsAhead =
+ (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs;
+ receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs;
+ if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
+ minNextPullTimeNs = receiverInfo->nextPullTimeNs;
}
+ } else {
+ VLOG("receiver already gone.");
}
}
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 3350736..807e4af 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -26,6 +26,7 @@
#include <vector>
#include "PullDataReceiver.h"
#include "StatsPuller.h"
+#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
namespace android {
@@ -36,11 +37,19 @@
// The field numbers of the fields that need to be summed when merging
// isolated uid with host uid.
std::vector<int> additiveFields;
- // How long should the puller wait before doing an actual pull again. Default
- // 1 sec. Set this to 0 if this is handled elsewhere.
+ // Minimum time before this puller does actual pull again.
+ // Pullers can cause significant impact to system health and battery.
+ // So that we don't pull too frequently.
+ // If a pull request comes before cooldown, a cached version from previous pull
+ // will be returned.
int64_t coolDownNs = 1 * NS_PER_SEC;
// The actual puller
sp<StatsPuller> puller;
+ // Max time allowed to pull this atom.
+ // We cannot reliably kill a pull thread. So we don't terminate the puller.
+ // The data is discarded if the pull takes longer than this and mHasGoodData
+ // marked as false.
+ int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs;
} PullAtomInfo;
class StatsPullerManager : public virtual RefBase {
@@ -61,13 +70,18 @@
// Verify if we know how to pull for this matcher
bool PullerForMatcherExists(int tagId) const;
- void OnAlarmFired(const int64_t timeNs);
+ void OnAlarmFired(int64_t elapsedTimeNs);
- // Use respective puller to pull the data. The returned data will have
- // elapsedTimeNs set as timeNs and will have wallClockTimeNs set as current
- // wall clock time.
- virtual bool Pull(const int tagId, const int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data);
+ // Pulls the most recent data.
+ // The data may be served from cache if consecutive pulls come within
+ // mCoolDownNs.
+ // Returns true if the pull was successful.
+ // Returns false when
+ // 1) the pull fails
+ // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+ // If the metric wants to make any change to the data, like timestamps, they
+ // should make a copy as this data may be shared with multiple metrics.
+ virtual bool Pull(int tagId, vector<std::shared_ptr<LogEvent>>* data);
// Clear pull data cache immediately.
int ForceClearPullerCache();
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.h b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
index 17ce5b4cb..87f5f02 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.h
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
@@ -29,6 +29,8 @@
class SubsystemSleepStatePuller : public StatsPuller {
public:
SubsystemSleepStatePuller();
+
+private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
};
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 3e5e82f..f4d0144 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -373,6 +373,16 @@
mPulledAtomStats[pullAtomId].dataError++;
}
+void StatsdStats::notePullTimeout(int pullAtomId) {
+ lock_guard<std::mutex> lock(mLock);
+ mPulledAtomStats[pullAtomId].pullTimeout++;
+}
+
+void StatsdStats::notePullExceedMaxDelay(int pullAtomId) {
+ lock_guard<std::mutex> lock(mLock);
+ mPulledAtomStats[pullAtomId].pullExceedMaxDelay++;
+}
+
void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
lock_guard<std::mutex> lock(mLock);
@@ -429,6 +439,8 @@
pullStats.second.maxPullDelayNs = 0;
pullStats.second.numPullDelay = 0;
pullStats.second.dataError = 0;
+ pullStats.second.pullTimeout = 0;
+ pullStats.second.pullExceedMaxDelay = 0;
}
}
@@ -535,13 +547,16 @@
dprintf(out, "********Pulled Atom stats***********\n");
for (const auto& pair : mPulledAtomStats) {
dprintf(out,
- "Atom %d->(total pull)%ld, (pull from cache)%ld, (min pull interval)%ld, (average "
- "pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay nanos)%lld, "
- "(max pull delay nanos)%lld, (data error)%ld\n",
+ "Atom %d->(total pull)%ld, (pull from cache)%ld, (min pull interval)%ld \n"
+ " (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
+ "nanos)%lld, "
+ " (max pull delay nanos)%lld, (data error)%ld\n"
+ " (pull timeout)%ld, (pull exceed max delay)%ld\n",
(int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
(long)pair.second.minPullIntervalSec, (long long)pair.second.avgPullTimeNs,
(long long)pair.second.maxPullTimeNs, (long long)pair.second.avgPullDelayNs,
- (long long)pair.second.maxPullDelayNs, pair.second.dataError);
+ (long long)pair.second.maxPullDelayNs, pair.second.dataError,
+ pair.second.pullTimeout, pair.second.pullExceedMaxDelay);
}
if (mAnomalyAlarmRegisteredStats > 0) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 3157037..dc647f8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -144,6 +144,8 @@
// How long to try to clear puller cache from last time
static const long kPullerCacheClearIntervalSec = 1;
+ // Max time to do a pull.
+ static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
/**
* Report a new config has been received and report the static stats about the config.
*
@@ -296,6 +298,16 @@
void notePullDelay(int pullAtomId, int64_t pullDelayNs);
/*
+ * Records pull exceeds timeout for the puller.
+ */
+ void notePullTimeout(int pullAtomId);
+
+ /*
+ * Records pull exceeds max delay for a metric.
+ */
+ void notePullExceedMaxDelay(int pullAtomId);
+
+ /*
* Records when system server restarts.
*/
void noteSystemServerRestart(int32_t timeSec);
@@ -335,6 +347,8 @@
int64_t maxPullDelayNs = 0;
long numPullDelay = 0;
long dataError = 0;
+ long pullTimeout = 0;
+ long pullExceedMaxDelay = 0;
} PulledAtomStats;
private:
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 8d61aba..2ff8aa1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,6 +41,14 @@
}
}
+LogEvent::LogEvent(const LogEvent& event) {
+ mTagId = event.mTagId;
+ mLogUid = event.mLogUid;
+ mElapsedTimestampNs = event.mElapsedTimestampNs;
+ mLogdTimestampNs = event.mLogdTimestampNs;
+ mValues = event.mValues;
+}
+
LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
mTagId = statsLogEventWrapper.getTagId();
mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 5408d17..43e6e4f 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -207,12 +207,15 @@
return &mValues;
}
+ inline LogEvent makeCopy() {
+ return LogEvent(*this);
+ }
+
private:
/**
- * Don't copy, it's slower. If we really need this we can add it but let's try to
- * avoid it.
+ * Only use this if copy is absolutely needed.
*/
- explicit LogEvent(const LogEvent&);
+ LogEvent(const LogEvent&);
/**
* Parses a log_msg into a LogEvent object.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index ec60244..67a1a47 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -68,15 +68,12 @@
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
-GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId,
- const int triggerAtomId, const int atomId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager)
+GaugeMetricProducer::GaugeMetricProducer(
+ const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
+ const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
@@ -86,6 +83,8 @@
mAtomId(atomId),
mIsPulled(pullTagId != -1),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
+ mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
+ : StatsdStats::kPullMaxDelayNs),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
StatsdStats::kAtomDimensionKeySizeLimitMap.end()
? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
@@ -338,14 +337,24 @@
return;
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
+ const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+ if (pullDelayNs > mMaxPullDelayNs) {
+ ALOGE("Pull finish too late for atom %d", mPullTagId);
+ StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+ return;
+ }
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
for (const auto& data : allData) {
- if (mEventMatcherWizard->matchLogEvent(
- *data, mWhatMatcherIndex) == MatchingState::kMatched) {
- onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+ LogEvent localCopy = data->makeCopy();
+ localCopy.setElapsedTimestampNs(timestampNs);
+ if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
+ MatchingState::kMatched) {
+ onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
}
}
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6e3530b..a1a5061 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -160,6 +160,8 @@
GaugeMetric::SamplingType mSamplingType;
+ const int64_t mMaxPullDelayNs;
+
// apply a whitelist on the original input
std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index cf56e2d..9a8e3bd 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -103,7 +103,9 @@
mValueDirection(metric.value_direction()),
mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
mUseZeroDefaultBase(metric.use_zero_default_base()),
- mHasGlobalBase(false) {
+ mHasGlobalBase(false),
+ mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
+ : StatsdStats::kPullMaxDelayNs) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -340,19 +342,32 @@
void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
vector<std::shared_ptr<LogEvent>> allData;
- if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
- for (const auto& data : allData) {
- if (mEventMatcherWizard->matchLogEvent(
- *data, mWhatMatcherIndex) == MatchingState::kMatched) {
- onMatchedLogEventLocked(mWhatMatcherIndex, *data);
- }
- }
- mHasGlobalBase = true;
- } else {
- // for pulled data, every pull is needed. So we reset the base if any
- // pull fails.
+ if (!mPullerManager->Pull(mPullTagId, &allData)) {
+ ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
resetBase();
+ return;
}
+ const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+ if (pullDelayNs > mMaxPullDelayNs) {
+ ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
+ (long long)mMaxPullDelayNs);
+ StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+ resetBase();
+ return;
+ }
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+
+ for (const auto& data : allData) {
+ // make a copy before doing and changes
+ LogEvent localCopy = data->makeCopy();
+ localCopy.setElapsedTimestampNs(timestampNs);
+ if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
+ MatchingState::kMatched) {
+ onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
+ }
+ }
+ mHasGlobalBase = true;
}
int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
@@ -381,10 +396,11 @@
return;
}
for (const auto& data : allData) {
- if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) ==
+ LogEvent localCopy = data->makeCopy();
+ if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
MatchingState::kMatched) {
- data->setElapsedTimestampNs(bucketEndTime);
- onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+ localCopy.setElapsedTimestampNs(bucketEndTime);
+ onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
}
}
mHasGlobalBase = true;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4991af4..4865aee 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -183,6 +183,8 @@
// diff against.
bool mHasGlobalBase;
+ const int64_t mMaxPullDelayNs;
+
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
@@ -207,6 +209,8 @@
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFail);
+ FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
};
} // namespace statsd
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index dffff7a..22883f3 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -131,8 +131,7 @@
VLOG("pull atom %d now", pullInfo.mPullerMatcher.atom_id());
vector<std::shared_ptr<LogEvent>> data;
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), nowMillis * 1000000L,
- &data);
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), &data);
VLOG("pulled %zu atoms", data.size());
if (data.size() > 0) {
writeToOutputLocked(data, pullInfo.mPullerMatcher);
diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h
index 73e4d33..b8185d2 100644
--- a/cmds/statsd/src/socket/StatsSocketListener.h
+++ b/cmds/statsd/src/socket/StatsSocketListener.h
@@ -35,7 +35,7 @@
class StatsSocketListener : public SocketListener, public virtual android::RefBase {
public:
- StatsSocketListener(const sp<LogListener>& listener);
+ explicit StatsSocketListener(const sp<LogListener>& listener);
virtual ~StatsSocketListener();
@@ -51,4 +51,4 @@
};
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 5a87e46..e8de875 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -401,6 +401,8 @@
optional int64 average_pull_delay_nanos = 7;
optional int64 max_pull_delay_nanos = 8;
optional int64 data_error = 9;
+ optional int64 pull_timeout = 10;
+ optional int64 pull_exceed_max_delay = 11;
}
repeated PulledAtomStats pulled_atom_stats = 10;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index f1310db..7de0bb3 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -64,6 +64,8 @@
const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7;
const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8;
const int FIELD_ID_DATA_ERROR = 9;
+const int FIELD_ID_PULL_TIMEOUT = 10;
+const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
namespace {
@@ -450,6 +452,10 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS,
(long long)pair.second.maxPullDelayNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT,
+ (long long)pair.second.pullTimeout);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY,
+ (long long)pair.second.pullExceedMaxDelay);
protoOutput->end(token);
}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index f485185..381ac32 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -240,7 +240,10 @@
optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ;
optional int64 min_bucket_size_nanos = 10;
+
optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
+
+ optional int32 max_pull_delay_sec = 13 [default = 10];
}
message ValueMetric {
@@ -285,6 +288,8 @@
optional ValueDirection value_direction = 13 [default = INCREASING];
optional bool skip_zero_diff_output = 14 [default = true];
+
+ optional int32 max_pull_delay_sec = 16 [default = 10];
}
message Alert {
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 2f19a02..90f641a 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -188,7 +188,9 @@
return false;
}
-void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
+void StorageManager::appendConfigMetricsReport(const ConfigKey& key,
+ ProtoOutputStream* proto,
+ bool erasa_data) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", STATS_DATA_DIR);
@@ -224,8 +226,9 @@
close(fd);
}
- // Remove file from disk after reading.
- remove(file_name.c_str());
+ if (erasa_data) {
+ remove(file_name.c_str());
+ }
}
}
}
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 8fbc89e..dcf3bb6 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -68,10 +68,12 @@
static bool hasConfigMetricsReport(const ConfigKey& key);
/**
- * Appends ConfigMetricsReport found on disk to the specific proto and
- * delete it.
+ * Appends the ConfigMetricsReport found on disk to the specifid proto
+ * and, if erase_data, deletes it from disk.
*/
- static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto);
+ static void appendConfigMetricsReport(const ConfigKey& key,
+ ProtoOutputStream* proto,
+ bool erase_data);
/**
* Call to load the saved configs from disk.
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index a7b4136..560fb9f 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -58,6 +58,45 @@
service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
+TEST(StatsServiceTest, TestGetUidFromArgs) {
+ Vector<String8> args;
+ args.push(String8("-1"));
+ args.push(String8("0"));
+ args.push(String8("1"));
+ args.push(String8("9999999999999999999999999999999999"));
+ args.push(String8("a1"));
+ args.push(String8(""));
+
+ int32_t uid;
+
+ StatsService service(nullptr);
+ service.mEngBuild = true;
+
+ // "-1"
+ EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+
+ // "0"
+ EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+ EXPECT_EQ(0, uid);
+
+ // "1"
+ EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+ EXPECT_EQ(1, uid);
+
+ // "999999999999999999"
+ EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
+
+ // "a1"
+ EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+
+ // ""
+ EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+
+ // For a non-userdebug, uid "1" cannot be impersonated.
+ service.mEngBuild = false;
+ EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 2d090e0..d5c358d 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -48,6 +48,7 @@
*gaugeMetric->mutable_dimensions_in_what() =
CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
gaugeMetric->set_bucket(FIVE_MINUTES);
+ gaugeMetric->set_max_pull_delay_sec(INT_MAX);
config.set_hash_strings_in_metric_report(false);
return config;
@@ -57,7 +58,7 @@
TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
- int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
@@ -202,7 +203,7 @@
TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
- int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
@@ -303,7 +304,7 @@
TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
- int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index abf1ab1..cab6eac 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -50,6 +50,7 @@
valueMetric->set_bucket(FIVE_MINUTES);
valueMetric->set_use_absolute_value_on_reset(true);
valueMetric->set_skip_zero_diff_output(false);
+ valueMetric->set_max_pull_delay_sec(INT_MAX);
return config;
}
@@ -57,7 +58,7 @@
TEST(ValueMetricE2eTest, TestPulledEvents) {
auto config = CreateStatsdConfig();
- int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
@@ -163,7 +164,7 @@
TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
auto config = CreateStatsdConfig();
- int64_t baseTimeNs = 10 * NS_PER_SEC;
+ int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
new file mode 100644
index 0000000..76e2097
--- /dev/null
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -0,0 +1,227 @@
+// 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <chrono>
+#include <thread>
+#include <vector>
+#include "../metrics/metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using std::this_thread::sleep_for;
+using testing::Contains;
+
+// cooldown time 1sec.
+int pullTagId = 10014;
+
+bool pullSuccess;
+vector<std::shared_ptr<LogEvent>> pullData;
+long pullDelayNs;
+
+class FakePuller : public StatsPuller {
+public:
+ FakePuller() : StatsPuller(pullTagId){};
+
+private:
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
+ (*data) = pullData;
+ sleep_for(std::chrono::nanoseconds(pullDelayNs));
+ return pullSuccess;
+ }
+};
+
+FakePuller puller;
+
+shared_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(pullTagId, eventTimeNs);
+ event->write(value);
+ event->init();
+ return event;
+}
+
+class StatsPullerTest : public ::testing::Test {
+public:
+ StatsPullerTest() {
+ }
+
+ void SetUp() override {
+ puller.ForceClearCache();
+ pullSuccess = false;
+ pullDelayNs = 0;
+ pullData.clear();
+ }
+};
+
+TEST_F(StatsPullerTest, PullSucces) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = true;
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ EXPECT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+ sleep_for(std::chrono::seconds(1));
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ pullSuccess = true;
+
+ EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
+ EXPECT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailAfterSuccess) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = true;
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ EXPECT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+ sleep_for(std::chrono::seconds(1));
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ pullSuccess = false;
+ dataHolder.clear();
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+
+ pullSuccess = true;
+ dataHolder.clear();
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+}
+
+// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown
+TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+ pullSuccess = true;
+ // timeout is 0.5
+ pullDelayNs = (long)(0.8 * NS_PER_SEC);
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ pullSuccess = true;
+ dataHolder.clear();
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullFail) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = false;
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTakeTooLong) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = true;
+ pullDelayNs = NS_PER_SEC;
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTooFast) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = true;
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ EXPECT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ pullSuccess = true;
+
+ dataHolder.clear();
+ EXPECT_TRUE(puller.Pull(&dataHolder));
+ EXPECT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ EXPECT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailsAndTooFast) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = false;
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ pullSuccess = true;
+
+ EXPECT_FALSE(puller.Pull(&dataHolder));
+ EXPECT_EQ(0, dataHolder.size());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 67a9f7f..2799107 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -90,6 +90,7 @@
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
metric.mutable_gauge_fields_filter()->set_include_all(false);
+ metric.set_max_pull_delay_sec(INT_MAX);
auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(1);
@@ -106,9 +107,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(3);
@@ -266,6 +266,7 @@
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
+ metric.set_max_pull_delay_sec(INT_MAX);
auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(2);
@@ -281,10 +282,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
.WillOnce(Return(false))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
event->write("some value");
@@ -341,6 +341,7 @@
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
+ metric.set_max_pull_delay_sec(INT_MAX);
auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(2);
@@ -357,9 +358,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write("some value");
@@ -420,6 +420,7 @@
metric.set_bucket(ONE_MINUTE);
metric.mutable_gauge_fields_filter()->set_include_all(true);
metric.set_condition(StringToId("APP_DIED"));
+ metric.set_max_pull_delay_sec(INT_MAX);
auto dim = metric.mutable_dimensions_in_what();
dim->set_field(tagId);
dim->add_child()->set_field(1);
@@ -454,9 +455,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(1000);
@@ -502,11 +502,12 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
+ metric.set_max_pull_delay_sec(INT_MAX);
auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(2);
@@ -591,6 +592,7 @@
metric.set_bucket(ONE_MINUTE);
metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
metric.mutable_gauge_fields_filter()->set_include_all(false);
+ metric.set_max_pull_delay_sec(INT_MAX);
auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(1);
@@ -604,9 +606,8 @@
new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(4);
@@ -614,8 +615,7 @@
data->push_back(event);
return true;
}))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
event->write(5);
@@ -664,6 +664,7 @@
metric.set_bucket(ONE_MINUTE);
metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
metric.mutable_gauge_fields_filter()->set_include_all(true);
+ metric.set_max_pull_delay_sec(INT_MAX);
auto dimensionMatcher = metric.mutable_dimensions_in_what();
// use field 1 as dimension.
dimensionMatcher->set_field(tagId);
@@ -678,9 +679,8 @@
new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3);
event->write(3);
@@ -689,8 +689,7 @@
data->push_back(event);
return true;
}))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(4);
@@ -699,8 +698,7 @@
data->push_back(event);
return true;
}))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
event->write(4);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 5524503..67570fc 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -125,6 +125,7 @@
metric.set_bucket(ONE_MINUTE);
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -136,9 +137,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
event->write(tagId);
@@ -218,6 +218,7 @@
metric.set_bucket(ONE_MINUTE);
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -232,9 +233,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
event->write(3);
@@ -315,6 +315,7 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_use_absolute_value_on_reset(true);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -326,7 +327,7 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -393,6 +394,7 @@
metric.set_bucket(ONE_MINUTE);
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -404,7 +406,7 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -469,6 +471,7 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -481,9 +484,8 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
event->write(tagId);
@@ -492,8 +494,7 @@
data->push_back(event);
return true;
}))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
event->write(tagId);
@@ -599,6 +600,7 @@
metric.set_bucket(ONE_MINUTE);
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -610,10 +612,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
.WillOnce(Return(true))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
event->write(tagId);
@@ -661,6 +662,7 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -672,9 +674,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
event->write(tagId);
@@ -683,8 +684,7 @@
data->push_back(event);
return true;
}))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
event->write(tagId);
@@ -924,6 +924,7 @@
metric.set_bucket(ONE_MINUTE);
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -935,7 +936,7 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId,
@@ -1012,6 +1013,7 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -1024,10 +1026,9 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
event->write(tagId);
@@ -1037,8 +1038,7 @@
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
event->write(tagId);
@@ -1098,6 +1098,7 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -1110,10 +1111,9 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
event->write(tagId);
@@ -1123,8 +1123,7 @@
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
event->write(tagId);
@@ -1134,8 +1133,7 @@
return true;
}))
// condition becomes true again
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
event->write(tagId);
@@ -1480,6 +1478,7 @@
metric.mutable_dimensions_in_what()->set_field(tagId);
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
metric.set_use_zero_default_base(true);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -1491,9 +1490,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
event->write(1);
@@ -1565,6 +1563,7 @@
metric.mutable_dimensions_in_what()->set_field(tagId);
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
metric.set_use_zero_default_base(true);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -1576,9 +1575,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
event->write(1);
@@ -1692,6 +1690,7 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.mutable_dimensions_in_what()->set_field(tagId);
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.set_max_pull_delay_sec(INT_MAX);
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -1703,9 +1702,8 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
- .WillOnce(Invoke([](int tagId, int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
event->write(1);
@@ -1804,6 +1802,128 @@
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
}
+TEST(ValueMetricProducerTest, TestResetBaseOnPullFail) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(INT_MAX);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
+ event->write(tagId);
+ event->write(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ .WillOnce(Return(false));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(100, curInterval.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ valueProducer.onConditionChanged(false, bucketStartTimeNs + 20);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasBase);
+ EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+ metric.set_max_pull_delay_sec(0);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.mCondition = true;
+ valueProducer.mHasGlobalBase = true;
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(110, curInterval.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+
+ valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(false, curInterval.hasBase);
+ EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 5afaba6..97c1072 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -38,8 +38,7 @@
MOCK_METHOD4(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver,
int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
- MOCK_METHOD3(Pull, bool(const int pullCode, const int64_t timeNs,
- vector<std::shared_ptr<LogEvent>>* data));
+ MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
};
class MockUidMap : public UidMap {
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index dd00561..a184f56 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -189,23 +189,22 @@
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(10016, _, _))
- .WillRepeatedly(
- Invoke([](int tagId, int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, timeNs);
- event->write(kUid1);
- event->write(kCpuTime1);
- event->init();
- data->push_back(event);
- // another event
- event = make_shared<LogEvent>(tagId, timeNs);
- event->write(kUid2);
- event->write(kCpuTime2);
- event->init();
- data->push_back(event);
- return true;
- }));
+ EXPECT_CALL(*pullerManager, Pull(10016, _))
+ .WillRepeatedly(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, 1111L);
+ event->write(kUid1);
+ event->write(kCpuTime1);
+ event->init();
+ data->push_back(event);
+ // another event
+ event = make_shared<LogEvent>(tagId, 1111L);
+ event->write(kUid2);
+ event->write(kCpuTime2);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
getExpectedShellData());
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index e3fe928..0775afe 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -16,207 +16,193 @@
package com.android.statsd.shelltools.testdrive;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.StatsLogReport;
import com.android.statsd.shelltools.Utils;
import com.google.common.io.Files;
-import com.google.protobuf.TextFormat;
-import com.google.protobuf.TextFormat.ParseException;
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TestDrive {
- public static final int PULL_ATOM_START = 10000;
- public static final long ATOM_MATCHER_ID = 1234567;
+ private static final int METRIC_ID_BASE = 1111;
+ private static final long ATOM_MATCHER_ID_BASE = 1234567;
+ private static final int PULL_ATOM_START = 10000;
+ private static final long CONFIG_ID = 54321;
+ private static final String[] ALLOWED_LOG_SOURCES = {
+ "AID_GRAPHICS",
+ "AID_INCIDENTD",
+ "AID_STATSD",
+ "AID_RADIO",
+ "com.android.systemui",
+ "com.android.vending",
+ "AID_SYSTEM",
+ "AID_ROOT",
+ "AID_BLUETOOTH",
+ "AID_LMKD"
+ };
+ private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
- public static final long CONFIG_ID = 54321;
-
- private static boolean mIsPushedAtom = false;
-
- private static final Logger logger = Logger.getLogger(TestDrive.class.getName());
+ private final Set<Long> mTrackedMetrics = new HashSet<>();
public static void main(String[] args) {
TestDrive testDrive = new TestDrive();
- Utils.setUpLogger(logger, false);
+ Set<Integer> trackedAtoms = new HashSet<>();
+ Utils.setUpLogger(LOGGER, false);
+ String remoteConfigPath = null;
- if (args.length != 1) {
- logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>");
+ if (args.length < 1) {
+ LOGGER.log(Level.SEVERE, "Usage: ./test_drive <atomId1> <atomId2> ... <atomIdN>");
return;
}
- int atomId;
- try {
- atomId = Integer.valueOf(args[0]);
- } catch (NumberFormatException e) {
- logger.log(Level.SEVERE, "Bad atom id provided: " + args[0]);
- return;
- }
- if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
- logger.log(Level.SEVERE, "No such atom found: " + args[0]);
- return;
- }
- mIsPushedAtom = atomId < PULL_ATOM_START;
+ for (int i = 0; i < args.length; i++) {
+ try {
+ int atomId = Integer.valueOf(args[i]);
+ if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
+ LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]);
+ continue;
+ }
+ trackedAtoms.add(atomId);
+ } catch (NumberFormatException e) {
+ LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]);
+ continue;
+ }
+ }
try {
- StatsdConfig config = testDrive.createConfig(atomId);
+ StatsdConfig config = testDrive.createConfig(trackedAtoms);
if (config == null) {
- logger.log(Level.SEVERE, "Failed to create valid config.");
+ LOGGER.log(Level.SEVERE, "Failed to create valid config.");
return;
}
- testDrive.pushConfig(config);
- logger.info("Pushed the following config to statsd:");
- logger.info(config.toString());
- if (mIsPushedAtom) {
- logger.info(
+ remoteConfigPath = testDrive.pushConfig(config);
+ LOGGER.info("Pushed the following config to statsd:");
+ LOGGER.info(config.toString());
+ if (!hasPulledAtom(trackedAtoms)) {
+ LOGGER.info(
"Now please play with the device to trigger the event. All events should "
+ "be dumped after 1 min ...");
Thread.sleep(60_000);
} else {
// wait for 2 min
- logger.info("Now wait for 2 minutes ...");
+ LOGGER.info("Now wait for 2 minutes ...");
Thread.sleep(120_000);
}
testDrive.dumpMetrics();
} catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to test drive: " + e.getMessage());
+ LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
} finally {
testDrive.removeConfig();
- }
- }
-
- private void pushConfig(StatsdConfig config) throws IOException, InterruptedException {
- File configFile = File.createTempFile("statsdconfig", ".config");
- configFile.deleteOnExit();
- Files.write(config.toByteArray(), configFile);
- String remotePath = "/data/local/tmp/" + configFile.getName();
- Utils.runCommand(null, logger, "adb", "push", configFile.getAbsolutePath(), remotePath);
- Utils.runCommand(null, logger,
- "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
- String.valueOf(CONFIG_ID));
- }
-
- private void removeConfig() {
- try {
- Utils.runCommand(null, logger,
- "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
- } catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
- }
- }
-
- private StatsdConfig createConfig(int atomId) {
- try {
- if (mIsPushedAtom) {
- return createSimpleEventMetricConfig(atomId);
- } else {
- return createSimpleGaugeMetricConfig(atomId);
+ if (remoteConfigPath != null) {
+ try {
+ Utils.runCommand(null, LOGGER, "adb", "shell", "rm", remoteConfigPath);
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING,
+ "Unable to remove remote config file: " + remoteConfigPath, e);
+ }
}
- } catch (ParseException e) {
- logger.log(
- Level.SEVERE,
- "Failed to parse the config! line: "
- + e.getLine()
- + " col: "
- + e.getColumn()
- + " "
- + e.getMessage());
}
- return null;
}
- private StatsdConfig createSimpleEventMetricConfig(int atomId) throws ParseException {
- StatsdConfig.Builder baseBuilder = getSimpleEventMetricBaseConfig();
- baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
- return baseBuilder.build();
+ private void dumpMetrics() throws Exception {
+ ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER);
+ // We may get multiple reports. Take the last one.
+ ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
+ for (StatsLogReport statsLog : report.getMetricsList()) {
+ if (mTrackedMetrics.contains(statsLog.getMetricId())) {
+ LOGGER.info(statsLog.toString());
+ }
+ }
}
- private StatsdConfig createSimpleGaugeMetricConfig(int atomId) throws ParseException {
- StatsdConfig.Builder baseBuilder = getSimpleGaugeMetricBaseConfig();
- baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
- return baseBuilder.build();
+ private StatsdConfig createConfig(Set<Integer> atomIds) {
+ long metricId = METRIC_ID_BASE;
+ long atomMatcherId = ATOM_MATCHER_ID_BASE;
+
+ StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+ builder
+ .addAllAllowedLogSource(Arrays.asList(ALLOWED_LOG_SOURCES))
+ .setHashStringsInMetricReport(false);
+
+ for (int atomId : atomIds) {
+ if (isPulledAtom(atomId)) {
+ builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+ GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
+ gaugeMetricBuilder
+ .setId(metricId)
+ .setWhat(atomMatcherId)
+ .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
+ .setBucket(TimeUnit.ONE_MINUTE);
+ builder.addGaugeMetric(gaugeMetricBuilder.build());
+ } else {
+ EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+ eventMetricBuilder
+ .setId(metricId)
+ .setWhat(atomMatcherId);
+ builder.addEventMetric(eventMetricBuilder.build());
+ builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+ }
+ atomMatcherId++;
+ mTrackedMetrics.add(metricId++);
+ }
+ return builder.build();
}
- private AtomMatcher createAtomMatcher(int atomId) {
+ private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
atomMatcherBuilder
- .setId(ATOM_MATCHER_ID)
+ .setId(matcherId)
.setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
return atomMatcherBuilder.build();
}
- private StatsdConfig.Builder getSimpleEventMetricBaseConfig() throws ParseException {
- StatsdConfig.Builder builder = StatsdConfig.newBuilder();
- TextFormat.merge(EVENT_BASE_CONFIG_SRTR, builder);
- return builder;
+ private static String pushConfig(StatsdConfig config) throws IOException, InterruptedException {
+ File configFile = File.createTempFile("statsdconfig", ".config");
+ configFile.deleteOnExit();
+ Files.write(config.toByteArray(), configFile);
+ String remotePath = "/data/local/tmp/" + configFile.getName();
+ Utils.runCommand(null, LOGGER, "adb", "push", configFile.getAbsolutePath(), remotePath);
+ Utils.runCommand(null, LOGGER,
+ "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
+ String.valueOf(CONFIG_ID));
+ return remotePath;
}
- private StatsdConfig.Builder getSimpleGaugeMetricBaseConfig() throws ParseException {
- StatsdConfig.Builder builder = StatsdConfig.newBuilder();
- TextFormat.merge(GAUGE_BASE_CONFIG_STR, builder);
- return builder;
- }
-
- private void dumpMetrics() throws Exception {
- ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, logger);
- // We may get multiple reports. Take the last one.
- ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
- // Really should be only one metric.
- if (report.getMetricsCount() != 1) {
- logger.log(Level.SEVERE,
- "Only one report metric expected, got " + report.getMetricsCount());
- return;
+ private static void removeConfig() {
+ try {
+ Utils.runCommand(null, LOGGER,
+ "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
}
-
- logger.info("Got following metric data dump:");
- logger.info(report.getMetrics(0).toString());
}
- private static final String EVENT_BASE_CONFIG_SRTR =
- "id: 12345\n"
- + "event_metric {\n"
- + " id: 1111\n"
- + " what: 1234567\n"
- + "}\n"
- + "allowed_log_source: \"AID_GRAPHICS\"\n"
- + "allowed_log_source: \"AID_INCIDENTD\"\n"
- + "allowed_log_source: \"AID_STATSD\"\n"
- + "allowed_log_source: \"AID_RADIO\"\n"
- + "allowed_log_source: \"com.android.systemui\"\n"
- + "allowed_log_source: \"com.android.vending\"\n"
- + "allowed_log_source: \"AID_SYSTEM\"\n"
- + "allowed_log_source: \"AID_ROOT\"\n"
- + "allowed_log_source: \"AID_BLUETOOTH\"\n"
- + "\n"
- + "hash_strings_in_metric_report: false";
+ private static boolean isPulledAtom(int atomId) {
+ return atomId >= PULL_ATOM_START;
+ }
- private static final String GAUGE_BASE_CONFIG_STR =
- "id: 56789\n"
- + "gauge_metric {\n"
- + " id: 2222\n"
- + " what: 1234567\n"
- + " gauge_fields_filter {\n"
- + " include_all: true\n"
- + " }\n"
- + " bucket: ONE_MINUTE\n"
- + "}\n"
- + "allowed_log_source: \"AID_GRAPHICS\"\n"
- + "allowed_log_source: \"AID_INCIDENTD\"\n"
- + "allowed_log_source: \"AID_STATSD\"\n"
- + "allowed_log_source: \"AID_RADIO\"\n"
- + "allowed_log_source: \"com.android.systemui\"\n"
- + "allowed_log_source: \"com.android.vending\"\n"
- + "allowed_log_source: \"AID_SYSTEM\"\n"
- + "allowed_log_source: \"AID_ROOT\"\n"
- + "allowed_log_source: \"AID_BLUETOOTH\"\n"
- + "\n"
- + "hash_strings_in_metric_report: false";
-
+ private static boolean hasPulledAtom(Set<Integer> atoms) {
+ for (Integer i : atoms) {
+ if (isPulledAtom(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index a39f5e3..4174ad7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -51,6 +51,8 @@
private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
"add-or-remove-call-companion-app";
private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
+ private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
+ "set-phone-acct-suggestion-component";
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -64,36 +66,37 @@
@Override
public void onShowUsage(PrintStream out) {
- out.println(
- "usage: telecom [subcommand] [options]\n" +
- "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
- "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
- "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
- "usage: telecom set-test-call-redirection-app <PACKAGE>\n" +
- "usage: telecom set-test-call-screening-app <PACKAGE>\n" +
- "usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
- "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
- "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" +
- "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" +
- "usage: telecom set-default-dialer <PACKAGE>\n" +
- "usage: telecom get-default-dialer\n" +
- "usage: telecom get-system-dialer\n" +
- "usage: telecom wait-on-handlers\n" +
- "\n" +
- "telecom set-phone-account-enabled: Enables the given phone account, if it has \n" +
- " already been registered with Telecom.\n" +
- "\n" +
- "telecom set-phone-account-disabled: Disables the given phone account, if it \n" +
- " has already been registered with telecom.\n" +
- "\n" +
- "telecom set-default-dialer: Sets the default dialer to the given component. \n" +
- "\n" +
- "telecom get-default-dialer: Displays the current default dialer. \n" +
- "\n" +
- "telecom get-system-dialer: Displays the current system dialer. \n" +
- "\n" +
- "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
- );
+ out.println("usage: telecom [subcommand] [options]\n"
+ + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
+ + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
+ + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
+ + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+ + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
+ + "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
+ + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
+ + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
+ + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
+ + " <LABEL> <ADDRESS>\n"
+ + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n"
+ + "usage: telecom set-default-dialer <PACKAGE>\n"
+ + "usage: telecom get-default-dialer\n"
+ + "usage: telecom get-system-dialer\n"
+ + "usage: telecom wait-on-handlers\n"
+ + "\n"
+ + "telecom set-phone-account-enabled: Enables the given phone account, if it has \n"
+ + " already been registered with Telecom.\n"
+ + "\n"
+ + "telecom set-phone-account-disabled: Disables the given phone account, if it \n"
+ + " has already been registered with telecom.\n"
+ + "\n"
+ + "telecom set-default-dialer: Sets the default dialer to the given component. \n"
+ + "\n"
+ + "telecom get-default-dialer: Displays the current default dialer. \n"
+ + "\n"
+ + "telecom get-system-dialer: Displays the current system dialer. \n"
+ + "\n"
+ + "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
+ );
}
@Override
@@ -134,6 +137,9 @@
case COMMAND_SET_TEST_AUTO_MODE_APP:
runSetTestAutoModeApp();
break;
+ case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
+ runSetTestPhoneAcctSuggestionComponent();
+ break;
case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
runRegisterSimPhoneAccount();
break;
@@ -216,6 +222,11 @@
mTelecomService.setTestAutoModeApp(packageName);
}
+ private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
+ final String componentName = nextArg();
+ mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
+ }
+
private void runUnregisterPhoneAccount() throws RemoteException {
final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
mTelecomService.unregisterPhoneAccount(handle);
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 5cd3d5d..7bbeb16 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1474,7 +1474,6 @@
Landroid/service/dreams/IDreamManager;->isDreaming()Z
Landroid/service/dreams/IDreamManager;->setDreamComponents([Landroid/content/ComponentName;)V
Landroid/service/euicc/IDeleteSubscriptionCallback;->onComplete(I)V
-Landroid/service/euicc/IDownloadSubscriptionCallback;->onComplete(I)V
Landroid/service/euicc/IEraseSubscriptionsCallback;->onComplete(I)V
Landroid/service/euicc/IEuiccService$Stub;-><init>()V
Landroid/service/euicc/IGetDefaultDownloadableSubscriptionListCallback;->onComplete(Landroid/service/euicc/GetDefaultDownloadableSubscriptionListResult;)V
diff --git a/core/java/android/annotation/Px.java b/core/java/android/annotation/Px.java
index a0ef224..ad99fdb 100644
--- a/core/java/android/annotation/Px.java
+++ b/core/java/android/annotation/Px.java
@@ -29,6 +29,8 @@
* Denotes that a numeric parameter, field or method return value is expected
* to represent a pixel dimension.
*
+ * @paramDoc This units of this value are pixels.
+ * @returnDoc This units of this value are pixels.
* {@hide}
*/
@Documented
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 84c7785..d423260 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1976,6 +1976,32 @@
}
/**
+ * Check if the context is allowed to start an activity on specified display. Some launch
+ * restrictions may apply to secondary displays that are private, virtual, or owned by the
+ * system, in which case an activity start may throw a {@link SecurityException}. Call this
+ * method prior to starting an activity on a secondary display to check if the current context
+ * has access to it.
+ *
+ * @see ActivityOptions#setLaunchDisplayId(int)
+ * @see android.view.Display.FLAG_PRIVATE
+ * @see android.view.Display.TYPE_VIRTUAL
+ *
+ * @param context Source context, from which an activity will be started.
+ * @param displayId Target display id.
+ * @param intent Intent used to launch an activity.
+ * @return {@code true} if a call to start an activity on the target display is allowed for the
+ * provided context and no {@link SecurityException} will be thrown, {@code false} otherwise.
+ */
+ public boolean isActivityStartAllowedOnDisplay(Context context, int displayId, Intent intent) {
+ try {
+ return getTaskService().isActivityStartAllowedOnDisplay(displayId, intent,
+ intent.resolveTypeIfNeeded(context.getContentResolver()), context.getUserId());
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failure from system", e);
+ }
+ }
+
+ /**
* Information you can retrieve about a particular Service that is
* currently running in the system.
*/
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a278423..78fe002 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2496,7 +2496,7 @@
* @param packageName The package performing the operation.
* @param result The result of the note.
*/
- void onOpNoted(String code, int uid, String packageName, int result);
+ void onOpNoted(int code, int uid, String packageName, int result);
}
/**
@@ -2953,7 +2953,7 @@
* @hide
*/
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
- public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) {
+ public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener callback) {
IAppOpsNotedCallback cb;
synchronized (mNotedWatchers) {
cb = mNotedWatchers.get(callback);
@@ -2963,17 +2963,13 @@
cb = new IAppOpsNotedCallback.Stub() {
@Override
public void opNoted(int op, int uid, String packageName, int mode) {
- callback.onOpNoted(sOpToString[op], uid, packageName, mode);
+ callback.onOpNoted(op, uid, packageName, mode);
}
};
mNotedWatchers.put(callback, cb);
}
try {
- final int[] opCodes = new int[ops.length];
- for (int i = 0; i < opCodes.length; i++) {
- opCodes[i] = strOpToOp(ops[i]);
- }
- mService.startWatchingNoted(opCodes, cb);
+ mService.startWatchingNoted(ops, cb);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2983,7 +2979,7 @@
* Stop watching for noted app ops. An app op may be immediate or long running.
* Unregistering a non-registered callback has no effect.
*
- * @see #startWatchingNoted(String[], OnOpNotedListener)
+ * @see #startWatchingNoted(int[], OnOpNotedListener)
* @see #noteOp(String, int, String)
*
* @hide
@@ -3078,7 +3074,7 @@
*/
public int unsafeCheckOpRaw(String op, int uid, String packageName) {
try {
- return mService.checkOperation(strOpToOp(op), uid, packageName);
+ return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 7fe21b2..b556033 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -16,7 +16,6 @@
package android.app;
-import android.annotation.NonNull;
import android.util.SparseIntArray;
import com.android.internal.util.function.QuadFunction;
@@ -37,10 +36,11 @@
* @param uid The UID for which to check.
* @param packageName The package for which to check.
* @param superImpl The super implementation.
+ * @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
* @return The app op check result.
*/
- int checkOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl);
+ int checkOperation(int code, int uid, String packageName, boolean raw,
+ QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl);
/**
* Allows overriding check audio operation behavior.
@@ -85,10 +85,7 @@
*
* @param code The op code to set.
* @param uid The UID for which to set.
- * @param packageName The package for which to set.
* @param mode The new mode to set.
- * @param isPrivileged If the package is privileged
*/
- public abstract void setMode(int code, int uid, @NonNull String packageName, int mode,
- boolean isPrivileged);
+ public abstract void setUidMode(int code, int uid, int mode);
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 777a494..f549e18 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -119,6 +119,8 @@
in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
+ boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType,
+ int userId);
void unhandledBack();
boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 00567523..163be8e 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -65,6 +65,10 @@
boolean areNotificationsEnabled(String pkg);
int getPackageImportance(String pkg);
+ void setAppOverlaysAllowed(String pkg, int uid, boolean allowed);
+ boolean areAppOverlaysAllowed(String pkg);
+ boolean areAppOverlaysAllowedForPackage(String pkg, int uid);
+
void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b9d5907..e066f06 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3130,6 +3130,10 @@
return mAppOverlayIntent;
}
+ /**
+ * Returns whether the platform is allowed (by the app developer) to generate contextual actions
+ * for this notification.
+ */
public boolean getAllowSystemGeneratedContextualActions() {
return mAllowSystemGeneratedContextualActions;
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 2322a42..34cd9f0 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -50,20 +50,12 @@
private static final String ATT_DESC = "desc";
private static final String ATT_ID = "id";
private static final String ATT_BLOCKED = "blocked";
- private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
private static final String ATT_USER_LOCKED = "locked";
- private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
-
/**
* @hide
*/
public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001;
- /**
- * @hide
- */
- @TestApi
- public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000002;
/**
* @see #getId()
@@ -74,7 +66,6 @@
private String mDescription;
private boolean mBlocked;
private List<NotificationChannel> mChannels = new ArrayList<>();
- private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
// Bitwise representation of fields that have been changed by the user
private int mUserLockedFields;
@@ -110,7 +101,6 @@
}
in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
mBlocked = in.readBoolean();
- mAllowAppOverlay = in.readBoolean();
mUserLockedFields = in.readInt();
}
@@ -138,7 +128,6 @@
}
dest.writeParcelableList(mChannels, flags);
dest.writeBoolean(mBlocked);
- dest.writeBoolean(mAllowAppOverlay);
dest.writeInt(mUserLockedFields);
}
@@ -181,15 +170,6 @@
}
/**
- * Returns whether notifications posted to this channel group can display outside of the
- * notification shade, in a floating window on top of other apps. These may additionally be
- * blocked at the notification channel level, see {@link NotificationChannel#canOverlayApps()}.
- */
- public boolean canOverlayApps() {
- return mAllowAppOverlay;
- }
-
- /**
* Sets the user visible description of this group.
*
* <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
@@ -200,21 +180,6 @@
}
/**
- * Sets whether notifications posted to this channel group can appear outside of the
- * notification shade, floating over other apps' content.
- *
- * <p>This value will be ignored for notifications that are posted to channels that do not
- * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
- *
- * <p>Only modifiable before the channel is submitted to
- * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.</p>
- * @see Notification#getAppOverlayIntent()
- */
- public void setAllowAppOverlay(boolean allowAppOverlay) {
- mAllowAppOverlay = allowAppOverlay;
- }
-
- /**
* @hide
*/
@TestApi
@@ -266,7 +231,6 @@
// Name, id, and importance are set in the constructor.
setDescription(parser.getAttributeValue(null, ATT_DESC));
setBlocked(safeBool(parser, ATT_BLOCKED, false));
- setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
}
private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
@@ -289,9 +253,6 @@
out.attribute(null, ATT_DESC, getDescription().toString());
}
out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
- if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
- out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
- }
out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields));
out.endTag(null, TAG_GROUP);
@@ -307,7 +268,6 @@
record.put(ATT_NAME, getName());
record.put(ATT_DESC, getDescription());
record.put(ATT_BLOCKED, isBlocked());
- record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
record.put(ATT_USER_LOCKED, mUserLockedFields);
return record;
}
@@ -336,7 +296,6 @@
if (o == null || getClass() != o.getClass()) return false;
NotificationChannelGroup that = (NotificationChannelGroup) o;
return isBlocked() == that.isBlocked() &&
- mAllowAppOverlay == that.mAllowAppOverlay &&
mUserLockedFields == that.mUserLockedFields &&
Objects.equals(getId(), that.getId()) &&
Objects.equals(getName(), that.getName()) &&
@@ -347,7 +306,7 @@
@Override
public int hashCode() {
return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(),
- mAllowAppOverlay, mUserLockedFields);
+ mUserLockedFields);
}
@Override
@@ -356,7 +315,6 @@
cloned.setDescription(getDescription());
cloned.setBlocked(isBlocked());
cloned.setChannels(getChannels());
- cloned.setAllowAppOverlay(canOverlayApps());
cloned.lockFields(mUserLockedFields);
return cloned;
}
@@ -369,7 +327,6 @@
+ ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
+ ", mBlocked=" + mBlocked
+ ", mChannels=" + mChannels
- + ", mAllowAppOverlay=" + mAllowAppOverlay
+ ", mUserLockedFields=" + mUserLockedFields
+ '}';
}
@@ -385,7 +342,6 @@
for (NotificationChannel channel : mChannels) {
channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
}
- proto.write(NotificationChannelGroupProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
proto.end(token);
}
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 306c366..aad3253 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1074,6 +1074,25 @@
}
}
+
+ /**
+ * Sets whether notifications posted by this app can appear outside of the
+ * notification shade, floating over other apps' content.
+ *
+ * <p>This value will be ignored for notifications that are posted to channels that do not
+ * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+ *
+ * @see Notification#getAppOverlayIntent()
+ */
+ public boolean areAppOverlaysAllowed() {
+ INotificationManager service = getService();
+ try {
+ return service.areAppOverlaysAllowed(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Checks the ability to modify notification do not disturb policy for the calling package.
*
@@ -1619,46 +1638,6 @@
return true;
}
- /**
- * @hide
- */
- public static boolean areAnyScreenOffEffectsSuppressed(int effects) {
- for (int i = 0; i < SCREEN_OFF_SUPPRESSED_EFFECTS.length; i++) {
- final int effect = SCREEN_OFF_SUPPRESSED_EFFECTS[i];
- if ((effects & effect) != 0) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @hide
- */
- public static boolean areAnyScreenOnEffectsSuppressed(int effects) {
- for (int i = 0; i < SCREEN_ON_SUPPRESSED_EFFECTS.length; i++) {
- final int effect = SCREEN_ON_SUPPRESSED_EFFECTS[i];
- if ((effects & effect) != 0) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @hide
- */
- public static int toggleScreenOffEffectsSuppressed(int currentEffects, boolean suppress) {
- return toggleEffects(currentEffects, SCREEN_OFF_SUPPRESSED_EFFECTS, suppress);
- }
-
- /**
- * @hide
- */
- public static int toggleScreenOnEffectsSuppressed(int currentEffects, boolean suppress) {
- return toggleEffects(currentEffects, SCREEN_ON_SUPPRESSED_EFFECTS, suppress);
- }
-
private static int toggleEffects(int currentEffects, int[] effects, boolean suppress) {
for (int i = 0; i < effects.length; i++) {
final int effect = effects[i];
@@ -1818,6 +1797,41 @@
return priorityMessageSenders;
}
+ /** @hide **/
+ public boolean showFullScreenIntents() {
+ return (suppressedVisualEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0;
+ }
+
+ /** @hide **/
+ public boolean showLights() {
+ return (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0;
+ }
+
+ /** @hide **/
+ public boolean showPeeking() {
+ return (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0;
+ }
+
+ /** @hide **/
+ public boolean showStatusBarIcons() {
+ return (suppressedVisualEffects & SUPPRESSED_EFFECT_STATUS_BAR) == 0;
+ }
+
+ /** @hide **/
+ public boolean showAmbient() {
+ return (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0;
+ }
+
+ /** @hide **/
+ public boolean showBadges() {
+ return (suppressedVisualEffects & SUPPRESSED_EFFECT_BADGE) == 0;
+ }
+
+ /** @hide **/
+ public boolean showInNotificationList() {
+ return (suppressedVisualEffects & SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0;
+ }
+
/**
* returns a deep copy of this policy
* @hide
@@ -1838,12 +1852,17 @@
* Recover a list of active notifications: ones that have been posted by the calling app that
* have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app.
*
- * Each notification is embedded in a {@link StatusBarNotification} object, including the
+ * <p><Each notification is embedded in a {@link StatusBarNotification} object, including the
* original <code>tag</code> and <code>id</code> supplied to
* {@link #notify(String, int, Notification) notify()}
* (via {@link StatusBarNotification#getTag() getTag()} and
* {@link StatusBarNotification#getId() getId()}) as well as a copy of the original
* {@link Notification} object (via {@link StatusBarNotification#getNotification()}).
+ * </p>
+ * <p>From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as an
+ * app's notification delegate via
+ * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+ * </p>
*
* @return An array of {@link StatusBarNotification}.
*/
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index c6086f1..a6f6d06 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -335,7 +335,8 @@
if (sService != null) {
try {
// All packages, current transport
- IRestoreSession binder = sService.beginRestoreSession(null, null);
+ IRestoreSession binder =
+ sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
if (binder != null) {
session = new RestoreSession(mContext, binder);
}
@@ -465,7 +466,7 @@
checkServiceBinder();
if (sService != null) {
try {
- return sService.getCurrentTransportComponent();
+ return sService.getCurrentTransportComponentForUser(mContext.getUserId());
} catch (RemoteException e) {
Log.e(TAG, "getCurrentTransportComponent() couldn't connect");
}
@@ -530,7 +531,8 @@
checkServiceBinder();
if (sService != null) {
try {
- sService.updateTransportAttributes(
+ sService.updateTransportAttributesForUser(
+ mContext.getUserId(),
transportComponent,
name,
configurationIntent,
@@ -590,7 +592,8 @@
try {
SelectTransportListenerWrapper wrapper = listener == null ?
null : new SelectTransportListenerWrapper(mContext, listener);
- sService.selectBackupTransportAsync(transport, wrapper);
+ sService.selectBackupTransportAsyncForUser(
+ mContext.getUserId(), transport, wrapper);
} catch (RemoteException e) {
Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
}
@@ -637,7 +640,7 @@
checkServiceBinder();
if (sService != null) {
try {
- return sService.getAvailableRestoreToken(packageName);
+ return sService.getAvailableRestoreTokenForUser(mContext.getUserId(), packageName);
} catch (RemoteException e) {
Log.e(TAG, "getAvailableRestoreToken() couldn't connect");
}
@@ -659,7 +662,7 @@
checkServiceBinder();
if (sService != null) {
try {
- return sService.isAppEligibleForBackup(packageName);
+ return sService.isAppEligibleForBackupForUser(mContext.getUserId(), packageName);
} catch (RemoteException e) {
Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect");
}
@@ -760,7 +763,7 @@
public Intent getConfigurationIntent(String transportName) {
if (sService != null) {
try {
- return sService.getConfigurationIntent(transportName);
+ return sService.getConfigurationIntentForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getConfigurationIntent() couldn't connect");
}
@@ -781,7 +784,7 @@
public String getDestinationString(String transportName) {
if (sService != null) {
try {
- return sService.getDestinationString(transportName);
+ return sService.getDestinationStringForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getDestinationString() couldn't connect");
}
@@ -802,7 +805,7 @@
public Intent getDataManagementIntent(String transportName) {
if (sService != null) {
try {
- return sService.getDataManagementIntent(transportName);
+ return sService.getDataManagementIntentForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getDataManagementIntent() couldn't connect");
}
@@ -825,7 +828,7 @@
public String getDataManagementLabel(String transportName) {
if (sService != null) {
try {
- return sService.getDataManagementLabel(transportName);
+ return sService.getDataManagementLabelForUser(mContext.getUserId(), transportName);
} catch (RemoteException e) {
Log.e(TAG, "getDataManagementLabel() couldn't connect");
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index f1e6b06..f8c5a81 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -43,6 +43,15 @@
* Any application can invoke this method for its own package, but
* only callers who hold the android.permission.BACKUP permission
* may invoke it for arbitrary packages.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the caller has made changes to its data.
+ */
+ void dataChangedForUser(int userId, String packageName);
+
+ /**
+ * {@link android.app.backup.IBackupManager.dataChangedForUser} for the calling user id.
*/
void dataChanged(String packageName);
@@ -53,6 +62,15 @@
* Any application can invoke this method for its own package, but
* only callers who hold the android.permission.BACKUP permission
* may invoke it for arbitrary packages.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which backup data should be erased.
+ */
+ void clearBackupDataForUser(int userId, String transportName, String packageName);
+
+ /**
+ * {@link android.app.backup.IBackupManager.clearBackupDataForUser} for the calling user id.
*/
void clearBackupData(String transportName, String packageName);
@@ -62,24 +80,59 @@
* operations.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the given transports should be initialized.
*/
- void initializeTransports(in String[] transportNames, IBackupObserver observer);
+ void initializeTransportsForUser(int userId, in String[] transportNames,
+ IBackupObserver observer);
/**
* Notifies the Backup Manager Service that an agent has become available. This
* method is only invoked by the Activity Manager.
+ *
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an agent has become available.
+ */
+ void agentConnectedForUser(int userId, String packageName, IBinder agent);
+
+ /**
+ * {@link android.app.backup.IBackupManager.agentConnected} for the calling user id.
*/
void agentConnected(String packageName, IBinder agent);
/**
* Notify the Backup Manager Service that an agent has unexpectedly gone away.
* This method is only invoked by the Activity Manager.
+ *
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which an agent has unexpectedly gone away.
+ */
+ void agentDisconnectedForUser(int userId, String packageName);
+
+ /**
+ * {@link android.app.backup.IBackupManager.agentDisconnected} for the calling user id.
*/
void agentDisconnected(String packageName);
/**
* Notify the Backup Manager Service that an application being installed will
* need a data-restore pass. This method is only invoked by the Package Manager.
+ *
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the application will need a data-restore pass.
+ */
+ void restoreAtInstallForUser(int userId, String packageName, int token);
+
+ /**
+ * {@link android.app.backup.IBackupManager.restoreAtInstallForUser} for the calling user id.
*/
void restoreAtInstall(String packageName, int token);
@@ -112,18 +165,19 @@
* is made generally available for launch.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which automatic restore should be enabled/disabled.
* @param doAutoRestore When true, enables the automatic app-data restore facility. When
* false, this facility will be disabled.
*/
- void setAutoRestore(boolean doAutoRestore);
+ void setAutoRestoreForUser(int userId, boolean doAutoRestore);
/**
- * Indicate that any necessary one-time provisioning has occurred.
- *
- * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * {@link android.app.backup.IBackupManager.setAutoRestoreForUser} for the calling user id.
*/
- void setBackupProvisioned(boolean isProvisioned);
+ void setAutoRestore(boolean doAutoRestore);
/**
* Report whether the backup mechanism is currently enabled.
@@ -220,9 +274,13 @@
* Perform a full-dataset backup of the given applications via the currently active
* transport.
*
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the full-dataset backup should be performed.
* @param packageNames The package names of the apps whose data are to be backed up.
*/
- void fullTransportBackup(in String[] packageNames);
+ void fullTransportBackupForUser(int userId, in String[] packageNames);
/**
* Restore device content from the data stream passed through the given socket. The
@@ -250,6 +308,18 @@
* backup dataset being used for restore.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the requested backup/restore operation can proceed.
+ */
+ void acknowledgeFullBackupOrRestoreForUser(int userId, int token, boolean allow,
+ in String curPassword, in String encryptionPassword,
+ IFullBackupRestoreObserver observer);
+
+ /**
+ * {@link android.app.backup.IBackupManager.acknowledgeFullBackupOrRestoreForUser} for the
+ * calling user id.
*/
void acknowledgeFullBackupOrRestore(int token, boolean allow,
in String curPassword, in String encryptionPassword,
@@ -260,7 +330,10 @@
* specified transport has not been bound at least once (for registration), this call will be
* ignored. Only the host process of the transport can change its description, otherwise a
* {@link SecurityException} will be thrown.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the attributes of the transport should be updated.
* @param transportComponent The identity of the transport being described.
* @param name A {@link String} with the new name for the transport. This is NOT for
* identification. MUST NOT be {@code null}.
@@ -279,13 +352,23 @@
* @throws SecurityException If the UID of the calling process differs from the package UID of
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
- void updateTransportAttributes(in ComponentName transportComponent, in String name,
+ void updateTransportAttributesForUser(int userId, in ComponentName transportComponent,
+ in String name,
in Intent configurationIntent, in String currentDestinationString,
in Intent dataManagementIntent, in String dataManagementLabel);
/**
* Identify the currently selected transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the currently selected transport should be identified.
+ */
+ String getCurrentTransportForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getCurrentTransportForUser} for the calling user id.
*/
String getCurrentTransport();
@@ -293,16 +376,35 @@
* 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. Callers must
* hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the currently selected transport should be identified.
*/
- ComponentName getCurrentTransportComponent();
+ ComponentName getCurrentTransportComponentForUser(int userId);
/**
* Request a list of all available backup transports' names. Callers must
* hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which all available backup transports' names should be listed.
+ */
+ String[] listAllTransportsForUser(int userId);
+
+ /**
+ * {@link android.app.backup.IBackupManager.listAllTransportsForUser} for the calling user id.
*/
String[] listAllTransports();
- ComponentName[] listAllTransportComponents();
+ /**
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which all available backup transports should be listed.
+ */
+ ComponentName[] listAllTransportComponentsForUser(int userId);
/**
* Retrieve the list of whitelisted transport components. Callers do </i>not</i> need
@@ -315,13 +417,22 @@
/**
* Specify the current backup transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the transport should be selected.
* @param transport The name of the transport to select. This should be one
* of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}.
* @return The name of the previously selected transport. If the given transport
* name is not one of the currently available transports, no change is made to
* the current transport setting and the method returns null.
*/
+ String selectBackupTransportForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.selectBackupTransportForUser} for the calling user
+ * id.
+ */
String selectBackupTransport(String transport);
/**
@@ -330,43 +441,85 @@
* which is in a separate process.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the transport should be selected.
* @param transport ComponentName of the service hosting the transport. This is different from
* the transport's name that is returned by {@link BackupTransport#name()}.
* @param listener A listener object to get a callback on the transport being selected.
*/
- void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
+ void selectBackupTransportAsyncForUser(int userId, in ComponentName transport,
+ ISelectBackupTransportCallback listener);
/**
* Get the configuration Intent, if any, from the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the configuration Intent should be reported.
* @param transport The name of the transport to query.
* @return An Intent to use with Activity#startActivity() to bring up the configuration
* UI supplied by the transport. If the transport has no configuration UI, it should
* return {@code null} here.
*/
+ Intent getConfigurationIntentForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getConfigurationIntentForUser} for the calling user
+ * id.
+ */
Intent getConfigurationIntent(String transport);
/**
* Get the destination string supplied by the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the transport destination string should be reported.
* @param transport The name of the transport to query.
* @return A string describing the current backup destination. This string is used
* verbatim by the Settings UI as the summary text of the "configure..." item.
*/
+ String getDestinationStringForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getDestinationStringForUser} for the calling user
+ * id.
+ */
String getDestinationString(String transport);
/**
* Get the manage-data UI intent, if any, from the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the manage-data UI intent should be reported.
+ */
+ Intent getDataManagementIntentForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getDataManagementIntentForUser} for the calling user
+ * id.
*/
Intent getDataManagementIntent(String transport);
/**
* Get the manage-data menu label, if any, from the given transport. Callers must
* hold the android.permission.BACKUP permission in order to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id for which the manage-data menu label should be reported.
+ */
+ String getDataManagementLabelForUser(int userId, String transport);
+
+ /**
+ * {@link android.app.backup.IBackupManager.getDataManagementLabelForUser} for the calling user
+ * id.
*/
String getDataManagementLabel(String transport);
@@ -381,7 +534,10 @@
* package. In that case, the restore session returned is suitable for supporting
* the BackupManager.requestRestore() functionality via RestoreSession.restorePackage()
* without requiring the app to hold any special permission.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which a restore session should be begun.
* @param packageName The name of the single package for which a restore will
* be requested. May be null, in which case all packages in the restore
* set can be restored.
@@ -389,7 +545,7 @@
* May be null, in which case the current active transport is used.
* @return An interface to the restore session, or null on error.
*/
- IRestoreSession beginRestoreSession(String packageName, String transportID);
+ IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
/**
* Notify the backup manager that a BackupAgent has completed the operation
@@ -427,13 +583,16 @@
* restored from if we were to install it right now.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which this operation should be performed.
* @param packageName The name of the package whose most-suitable dataset we
* wish to look up
* @return The dataset token from which a restore should be attempted, or zero if
* no suitable data is available.
*/
- long getAvailableRestoreToken(String packageName);
+ long getAvailableRestoreTokenForUser(int userId, String packageName);
/**
* Ask the framework whether this app is eligible for backup.
@@ -442,21 +601,27 @@
* {@link #filterAppsEligibleForBackup(String[])} to save resources.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which this operation should be performed.
* @param packageName The name of the package.
* @return Whether this app is eligible for backup.
*/
- boolean isAppEligibleForBackup(String packageName);
+ boolean isAppEligibleForBackupForUser(int userId, String packageName);
/**
* Filter the packages that are eligible for backup and return the result.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
*
+ * @param userId User id for which the filter should be performed.
* @param packages The list of packages to filter.
* @return The packages eligible for backup.
*/
- String[] filterAppsEligibleForBackup(in String[] packages);
+ String[] filterAppsEligibleForBackupForUser(int userId, in String[] packages);
/**
* Request an immediate backup, providing an observer to which results of the backup operation
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e08d405..adedff3 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -67,7 +67,9 @@
}
// Legacy api - getDefaultAdapter does not take in the context
mAdapter = BluetoothAdapter.getDefaultAdapter();
- mAdapter.setContext(context);
+ if (mAdapter != null) {
+ mAdapter.setContext(context);
+ }
}
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 001e328..6f12cad 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -691,7 +691,8 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
*/
- public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+ @NonNull
+ public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(attrs);
}
@@ -702,8 +703,9 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
*/
- public final TypedArray obtainStyledAttributes(
- @StyleRes int resid, @StyleableRes int[] attrs) throws Resources.NotFoundException {
+ @NonNull
+ public final TypedArray obtainStyledAttributes(@StyleRes int resid,
+ @NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException {
return getTheme().obtainStyledAttributes(resid, attrs);
}
@@ -714,8 +716,9 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
*/
+ @NonNull
public final TypedArray obtainStyledAttributes(
- AttributeSet set, @StyleableRes int[] attrs) {
+ @Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
}
@@ -726,8 +729,9 @@
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
*/
- public final TypedArray obtainStyledAttributes(
- AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+ @NonNull
+ public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+ @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
return getTheme().obtainStyledAttributes(
set, attrs, defStyleAttr, defStyleRes);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2f0618c..d5c6c63 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4222,6 +4222,11 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN";
/**
+ * The home activity shown on secondary displays that support showing home activities.
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME";
+ /**
* This is the setup wizard activity, that is the first activity that is displayed
* when the user sets up the device for the first time.
* @hide
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f81eb76..f06df3d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -54,6 +55,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Iterator;
@@ -142,6 +145,15 @@
public static final String ACTION_SESSION_COMMITTED =
"android.content.pm.action.SESSION_COMMITTED";
+ /**
+ * Broadcast Action: Send information about a staged install session when its state is updated.
+ * <p>
+ * The associated session information is defined in {@link #EXTRA_SESSION}.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SESSION_UPDATED =
+ "android.content.pm.action.SESSION_UPDATED";
+
/** {@hide} */
public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
@@ -1594,6 +1606,29 @@
public static final int INVALID_ID = -1;
/** {@hide} */
private static final int[] NO_SESSIONS = {};
+
+ /** @hide */
+ @IntDef(value = {NO_ERROR, VERIFICATION_FAILED, ACTIVATION_FAILED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StagedSessionErrorCode{}
+ /**
+ * Constant indicating that no error occurred during the preparation or the activation of
+ * this staged session.
+ */
+ public static final int NO_ERROR = 0;
+
+ /**
+ * Constant indicating that an error occurred during the verification phase (pre-reboot) of
+ * this staged session.
+ */
+ public static final int VERIFICATION_FAILED = 1;
+
+ /**
+ * Constant indicating that an error occurred during the activation phase (post-reboot) of
+ * this staged session.
+ */
+ public static final int ACTIVATION_FAILED = 2;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int sessionId;
@@ -1653,6 +1688,14 @@
public int[] childSessionIds = NO_SESSIONS;
/** {@hide} */
+ public boolean isSessionApplied;
+ /** {@hide} */
+ public boolean isSessionReady;
+ /** {@hide} */
+ public boolean isSessionFailed;
+ private int mStagedSessionErrorCode;
+
+ /** {@hide} */
@UnsupportedAppUsage
public SessionInfo() {
}
@@ -1686,6 +1729,10 @@
if (childSessionIds == null) {
childSessionIds = NO_SESSIONS;
}
+ isSessionApplied = source.readBoolean();
+ isSessionReady = source.readBoolean();
+ isSessionFailed = source.readBoolean();
+ mStagedSessionErrorCode = source.readInt();
}
/**
@@ -1970,6 +2017,44 @@
return childSessionIds;
}
+ /**
+ * Whether the staged session has been applied successfully, meaning that all of its
+ * packages have been activated and no further action is required.
+ * Only meaningful if {@code isStaged} is true.
+ */
+ public boolean isSessionApplied() {
+ return isSessionApplied;
+ }
+
+ /**
+ * Whether the staged session is ready to be applied at next reboot. Only meaningful if
+ * {@code isStaged} is true.
+ */
+ public boolean isSessionReady() {
+ return isSessionReady;
+ }
+
+ /**
+ * Whether something went wrong and the staged session is declared as failed, meaning that
+ * it will be ignored at next reboot. Only meaningful if {@code isStaged} is true.
+ */
+ public boolean isSessionFailed() {
+ return isSessionFailed;
+ }
+
+ /**
+ * If something went wrong with a staged session, clients can check this error code to
+ * understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
+ */
+ public int getStagedSessionErrorCode() {
+ return mStagedSessionErrorCode;
+ }
+
+ /** {@hide} */
+ public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode) {
+ mStagedSessionErrorCode = errorCode;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -2001,6 +2086,10 @@
dest.writeBoolean(isStaged);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
+ dest.writeBoolean(isSessionApplied);
+ dest.writeBoolean(isSessionReady);
+ dest.writeBoolean(isSessionFailed);
+ dest.writeInt(mStagedSessionErrorCode);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index ad82626d..35609c9 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -33,11 +33,29 @@
* This class provides information for a shared library. There are
* three types of shared libraries: builtin - non-updatable part of
* the OS; dynamic - updatable backwards-compatible dynamically linked;
- * static - updatable non backwards-compatible emulating static linking.
+ * static - non backwards-compatible emulating static linking.
*/
public final class SharedLibraryInfo implements Parcelable {
/** @hide */
+ public static SharedLibraryInfo createForStatic(PackageParser.Package pkg) {
+ return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(),
+ pkg.staticSharedLibName,
+ pkg.staticSharedLibVersion,
+ TYPE_STATIC,
+ new VersionedPackage(pkg.manifestPackageName, pkg.getLongVersionCode()),
+ null, null);
+ }
+
+ /** @hide */
+ public static SharedLibraryInfo createForDynamic(PackageParser.Package pkg, String name) {
+ return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(), name,
+ (long) VERSION_UNDEFINED,
+ TYPE_DYNAMIC, new VersionedPackage(pkg.packageName, pkg.getLongVersionCode()),
+ null, null);
+ }
+
+ /** @hide */
@IntDef(flag = true, prefix = { "TYPE_" }, value = {
TYPE_BUILTIN,
TYPE_DYNAMIC,
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 88b1c88..365ceac 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1468,7 +1468,8 @@
* @see #obtainStyledAttributes(int, int[])
* @see #obtainStyledAttributes(AttributeSet, int[], int, int)
*/
- public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
+ @NonNull
+ public TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
}
@@ -1493,7 +1494,9 @@
* @see #obtainStyledAttributes(int[])
* @see #obtainStyledAttributes(AttributeSet, int[], int, int)
*/
- public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
+ @NonNull
+ public TypedArray obtainStyledAttributes(@StyleRes int resId,
+ @NonNull @StyleableRes int[] attrs)
throws NotFoundException {
return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
}
@@ -1547,8 +1550,10 @@
* @see #obtainStyledAttributes(int[])
* @see #obtainStyledAttributes(int, int[])
*/
- public TypedArray obtainStyledAttributes(AttributeSet set,
- @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ @NonNull
+ public TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
+ @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+ @StyleRes int defStyleRes) {
return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
}
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 7e52ca3..be054297 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -19,26 +19,54 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
/** @hide */
@SystemApi
@TestApi
public final class BrightnessConfiguration implements Parcelable {
+ private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+ private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
+ private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections";
+ private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction";
+ private static final String ATTR_LUX = "lux";
+ private static final String ATTR_NITS = "nits";
+ private static final String ATTR_DESCRIPTION = "description";
+ private static final String ATTR_PACKAGE_NAME = "package-name";
+ private static final String ATTR_CATEGORY = "category";
+
private final float[] mLux;
private final float[] mNits;
+ private final Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private final String mDescription;
- private BrightnessConfiguration(float[] lux, float[] nits, String description) {
+ private BrightnessConfiguration(float[] lux, float[] nits,
+ Map<String, BrightnessCorrection> correctionsByPackageName,
+ Map<Integer, BrightnessCorrection> correctionsByCategory, String description) {
mLux = lux;
mNits = nits;
+ mCorrectionsByPackageName = correctionsByPackageName;
+ mCorrectionsByCategory = correctionsByCategory;
mDescription = description;
}
@@ -56,6 +84,38 @@
}
/**
+ * Returns a brightness correction by app, or null.
+ *
+ * @param packageName
+ * The app's package name.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+ return mCorrectionsByPackageName.get(packageName);
+ }
+
+ /**
+ * Returns a brightness correction by app category, or null.
+ *
+ * @param category
+ * The app category.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByCategory(int category) {
+ return mCorrectionsByCategory.get(category);
+ }
+
+ /**
* Returns description string.
* @hide
*/
@@ -67,6 +127,20 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloatArray(mLux);
dest.writeFloatArray(mNits);
+ dest.writeInt(mCorrectionsByPackageName.size());
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeString(packageName);
+ correction.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mCorrectionsByCategory.size());
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeInt(category);
+ correction.writeToParcel(dest, flags);
+ }
dest.writeString(mDescription);
}
@@ -85,7 +159,14 @@
}
sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
}
- sb.append("], '");
+ sb.append("], {");
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", ");
+ }
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ sb.append(entry.getKey() + ": " + entry.getValue() + ", ");
+ }
+ sb.append("}, '");
if (mDescription != null) {
sb.append(mDescription);
}
@@ -98,6 +179,8 @@
int result = 1;
result = result * 31 + Arrays.hashCode(mLux);
result = result * 31 + Arrays.hashCode(mNits);
+ result = result * 31 + mCorrectionsByPackageName.hashCode();
+ result = result * 31 + mCorrectionsByCategory.hashCode();
if (mDescription != null) {
result = result * 31 + mDescription.hashCode();
}
@@ -114,6 +197,8 @@
}
final BrightnessConfiguration other = (BrightnessConfiguration) o;
return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
+ && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName)
+ && mCorrectionsByCategory.equals(other.mCorrectionsByCategory)
&& Objects.equals(mDescription, other.mDescription);
}
@@ -123,7 +208,25 @@
float[] lux = in.createFloatArray();
float[] nits = in.createFloatArray();
Builder builder = new Builder(lux, nits);
- builder.setDescription(in.readString());
+
+ int n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final String packageName = in.readString();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+
+ n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final int category = in.readInt();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByCategory(category, correction);
+ }
+
+ final String description = in.readString();
+ builder.setDescription(description);
return builder.build();
}
@@ -133,11 +236,146 @@
};
/**
+ * Writes the configuration to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+ if (mDescription != null) {
+ serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
+ }
+ for (int i = 0; i < mLux.length; i++) {
+ serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+ serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
+ serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
+ serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ for (Map.Entry<String, BrightnessCorrection> entry :
+ mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ }
+
+ /**
+ * Read a configuration from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String description = null;
+ List<Float> luxList = new ArrayList<>();
+ List<Float> nitsList = new ArrayList<>();
+ Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
+ Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>();
+ final int configDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, configDepth)) {
+ if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+ description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+ final int curveDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, curveDepth)) {
+ if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+ continue;
+ }
+ final float lux = loadFloatFromXml(parser, ATTR_LUX);
+ final float nits = loadFloatFromXml(parser, ATTR_NITS);
+ luxList.add(lux);
+ nitsList.add(nits);
+ }
+ }
+ if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) {
+ final int correctionsDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, correctionsDepth)) {
+ if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) {
+ continue;
+ }
+ final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
+ BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
+ if (packageName != null) {
+ correctionsByPackageName.put(packageName, correction);
+ } else if (categoryText != null) {
+ try {
+ final int category = Integer.parseInt(categoryText);
+ correctionsByCategory.put(category, correction);
+ } catch (NullPointerException | NumberFormatException e) {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ final int n = luxList.size();
+ float[] lux = new float[n];
+ float[] nits = new float[n];
+ for (int i = 0; i < n; i++) {
+ lux[i] = luxList.get(i);
+ nits[i] = nitsList.get(i);
+ }
+ final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux,
+ nits);
+ builder.setDescription(description);
+ for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByCategory(category, correction);
+ }
+ return builder.build();
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ /**
* A builder class for {@link BrightnessConfiguration}s.
*/
public static class Builder {
+ private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20;
+ private static final int MAX_CORRECTIONS_BY_CATEGORY = 20;
+
private float[] mCurveLux;
private float[] mCurveNits;
+ private Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private String mDescription;
/**
@@ -169,6 +407,88 @@
checkMonotonic(nits, false /*strictly increasing*/, "nits");
mCurveLux = lux;
mCurveNits = nits;
+ mCorrectionsByPackageName = new HashMap<>();
+ mCorrectionsByCategory = new HashMap<>();
+ }
+
+ /**
+ * Returns the maximum number of corrections by package name allowed.
+ *
+ * @return The maximum number of corrections by package name allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByPackageName() {
+ return MAX_CORRECTIONS_BY_PACKAGE_NAME;
+ }
+
+ /**
+ * Returns the maximum number of corrections by category allowed.
+ *
+ * @return The maximum number of corrections by category allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByCategory() {
+ return MAX_CORRECTIONS_BY_CATEGORY;
+ }
+
+ /**
+ * Add a brightness correction by app package name.
+ * This correction is applied whenever an app with this package name has the top activity
+ * of the focused stack.
+ *
+ * @param packageName
+ * The app's package name.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentExceptions
+ * Maximum number of corrections by package name exceeded (see
+ * {@link #getMaxCorrectionsByPackageName}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByPackageName(String packageName,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
+ throw new IllegalArgumentException("Too many corrections by package name");
+ }
+ mCorrectionsByPackageName.put(packageName, correction);
+ return this;
+ }
+
+ /**
+ * Add a brightness correction by app category.
+ * This correction is applied whenever an app with this category has the top activity of
+ * the focused stack, and only if a correction by package name has not been applied.
+ *
+ * @param category
+ * The {@link android.content.pm.ApplicationInfo#category app category}.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentException
+ * Maximum number of corrections by category exceeded (see
+ * {@link #getMaxCorrectionsByCategory}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
+ throw new IllegalArgumentException("Too many corrections by category");
+ }
+ mCorrectionsByCategory.put(category, correction);
+ return this;
}
/**
@@ -191,7 +511,8 @@
if (mCurveLux == null || mCurveNits == null) {
throw new IllegalStateException("A curve must be set!");
}
- return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
+ return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName,
+ mCorrectionsByCategory, mDescription);
}
private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/display/BrightnessCorrection.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/hardware/display/BrightnessCorrection.aidl
index b7a7920..3abe29c 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/display/BrightnessCorrection.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2015, The Android Open Source Project
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.hardware.usb;
+package android.hardware.display;
-parcelable UsbPort;
+parcelable BrightnessCorrection;
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
new file mode 100644
index 0000000..c4e0e3b
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.MathUtils;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
+ * actual correction scheme.
+ * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
+ * name and category) to corrections that need to be applied to the brightness within that context.
+ * Corrections are currently done by the app that has the top activity of the focused stack, either
+ * by its package name, or (if its package name is not mapped to any correction) by its category.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BrightnessCorrection implements Parcelable {
+
+ private static final int SCALE_AND_TRANSLATE_LOG = 1;
+
+ private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
+
+ private BrightnessCorrectionImplementation mImplementation;
+
+ // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
+ // make this class abstract and use composition instead of inheritence.
+ private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
+ mImplementation = implementation;
+ }
+
+ /**
+ * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @param scale
+ * How much to scale the log brightness.
+ * @param translate
+ * How much to translate the log brightness.
+ *
+ * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @throws IllegalArgumentException
+ * - scale or translate are NaN.
+ */
+ @NonNull
+ public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
+ BrightnessCorrectionImplementation implementation =
+ new ScaleAndTranslateLog(scale, translate);
+ return new BrightnessCorrection(implementation);
+ }
+
+ /**
+ * Applies the brightness correction to a given brightness.
+ *
+ * @param brightness
+ * The brightness.
+ *
+ * @return The corrected brightness.
+ */
+ public float apply(float brightness) {
+ return mImplementation.apply(brightness);
+ }
+
+ /**
+ * Returns a string representation.
+ *
+ * @return A string representation.
+ */
+ public String toString() {
+ return mImplementation.toString();
+ }
+
+ public static final Creator<BrightnessCorrection> CREATOR =
+ new Creator<BrightnessCorrection>() {
+ public BrightnessCorrection createFromParcel(Parcel in) {
+ final int type = in.readInt();
+ switch (type) {
+ case SCALE_AND_TRANSLATE_LOG:
+ return ScaleAndTranslateLog.readFromParcel(in);
+ }
+ return null;
+ }
+
+ public BrightnessCorrection[] newArray(int size) {
+ return new BrightnessCorrection[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mImplementation.writeToParcel(dest);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes the correction to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ mImplementation.saveToXml(serializer);
+ }
+
+ /**
+ * Read a correction from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
+ return ScaleAndTranslateLog.loadFromXml(parser);
+ }
+ }
+ return null;
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ private interface BrightnessCorrectionImplementation {
+ float apply(float brightness);
+ String toString();
+ void writeToParcel(Parcel dest);
+ void saveToXml(XmlSerializer serializer) throws IOException;
+ // Package-private static methods:
+ // static BrightnessCorrection readFromParcel(Parcel in);
+ // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ // XmlPullParserException;
+ }
+
+ /**
+ * A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ */
+ private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
+ private static final float MIN_SCALE = 0.5f;
+ private static final float MAX_SCALE = 2.0f;
+ private static final float MIN_TRANSLATE = -0.6f;
+ private static final float MAX_TRANSLATE = 0.7f;
+
+ private static final String ATTR_SCALE = "scale";
+ private static final String ATTR_TRANSLATE = "translate";
+
+ private final float mScale;
+ private final float mTranslate;
+
+ ScaleAndTranslateLog(float scale, float translate) {
+ if (Float.isNaN(scale) || Float.isNaN(translate)) {
+ throw new IllegalArgumentException("scale and translate must be numbers");
+ }
+ mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
+ }
+
+ @Override
+ public float apply(float brightness) {
+ return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
+ }
+
+ @Override
+ public String toString() {
+ return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest) {
+ dest.writeInt(SCALE_AND_TRANSLATE_LOG);
+ dest.writeFloat(mScale);
+ dest.writeFloat(mTranslate);
+ }
+
+ @Override
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
+ serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
+ serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ }
+
+ static BrightnessCorrection readFromParcel(Parcel in) {
+ float scale = in.readFloat();
+ float translate = in.readFloat();
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+
+ static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final float scale = loadFloatFromXml(parser, ATTR_SCALE);
+ final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+ }
+}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index f4e776c..edc3f94 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -20,7 +20,7 @@
import android.content.ComponentName;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbPort;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbPortStatus;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -112,7 +112,7 @@
ParcelFileDescriptor getControlFd(long function);
/* Gets the list of USB ports. */
- UsbPort[] getPorts();
+ List<ParcelableUsbPort> getPorts();
/* Gets the status of the specified USB port. */
UsbPortStatus getPortStatus(in String portId);
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
similarity index 87%
rename from core/java/android/hardware/usb/UsbPort.aidl
rename to core/java/android/hardware/usb/ParcelableUsbPort.aidl
index b7a7920..4431551 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015, The Android Open Source Project
+ * 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.
@@ -16,4 +16,4 @@
package android.hardware.usb;
-parcelable UsbPort;
+parcelable ParcelableUsbPort;
diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java
new file mode 100644
index 0000000..7f7ba96
--- /dev/null
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * A parcelable wrapper to send UsbPorts over binders.
+ *
+ * @hide
+ */
+@Immutable
+public final class ParcelableUsbPort implements Parcelable {
+ private final @NonNull String mId;
+ private final int mSupportedModes;
+
+ private ParcelableUsbPort(@NonNull String id, int supportedModes) {
+ mId = id;
+ mSupportedModes = supportedModes;
+ }
+
+ /**
+ * Create the parcelable version of a {@link UsbPort}.
+ *
+ * @param port The port to create a parcealable version of
+ *
+ * @return The parcelable version of the port
+ */
+ public static @NonNull ParcelableUsbPort of(@NonNull UsbPort port) {
+ return new ParcelableUsbPort(port.getId(), port.getSupportedModes());
+ }
+
+ /**
+ * Create a {@link UsbPort} from this object.
+ *
+ * @param usbManager A link to the usbManager in the current context
+ *
+ * @return The UsbPort for this object
+ */
+ public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) {
+ return new UsbPort(usbManager, mId, mSupportedModes);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mSupportedModes);
+ }
+
+ public static final Creator<ParcelableUsbPort> CREATOR =
+ new Creator<ParcelableUsbPort>() {
+ @Override
+ public ParcelableUsbPort createFromParcel(Parcel in) {
+ String id = in.readString();
+ int supportedModes = in.readInt();
+ return new ParcelableUsbPort(id, supportedModes);
+ }
+
+ @Override
+ public ParcelableUsbPort[] newArray(int size) {
+ return new ParcelableUsbPort[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 4111941..6014478 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
package android.hardware.usb;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -39,9 +40,10 @@
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
@@ -97,15 +99,11 @@
* Broadcast Action: A broadcast for USB port changes.
*
* This intent is sent when a USB port is added, removed, or changes state.
- * <ul>
- * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
- * for the port.
- * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
- * for the port, or null if the port has been removed
- * </ul>
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
public static final String ACTION_USB_PORT_CHANGED =
"android.hardware.usb.action.USB_PORT_CHANGED";
@@ -796,34 +794,44 @@
* device class (which supports all types of ports despite its name).
* </p>
*
- * @return The list of USB ports, or null if none.
+ * @return The list of USB ports
*
* @hide
*/
- @UnsupportedAppUsage
- public UsbPort[] getPorts() {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @NonNull List<UsbPort> getPorts() {
if (mService == null) {
- return null;
+ return Collections.emptyList();
}
+
+ List<ParcelableUsbPort> parcelablePorts;
try {
- return mService.getPorts();
+ parcelablePorts = mService.getPorts();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ if (parcelablePorts == null) {
+ return Collections.emptyList();
+ } else {
+ int numPorts = parcelablePorts.size();
+
+ ArrayList<UsbPort> ports = new ArrayList<>(numPorts);
+ for (int i = 0; i < numPorts; i++) {
+ ports.add(parcelablePorts.get(i).getUsbPort(this));
+ }
+
+ return ports;
+ }
}
/**
- * Gets the status of the specified USB port.
- *
- * @param port The port to query.
- * @return The status of the specified USB port, or null if unknown.
+ * Should only be called by {@link UsbPort#getStatus}.
*
* @hide
*/
- @UnsupportedAppUsage
- public UsbPortStatus getPortStatus(UsbPort port) {
- Preconditions.checkNotNull(port, "port must not be null");
-
+ UsbPortStatus getPortStatus(UsbPort port) {
try {
return mService.getPortStatus(port.getId());
} catch (RemoteException e) {
@@ -832,29 +840,11 @@
}
/**
- * Sets the desired role combination of the port.
- * <p>
- * The supported role combinations depend on what is connected to the port and may be
- * determined by consulting
- * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
- * </p><p>
- * Note: This function is asynchronous and may fail silently without applying
- * the requested changes. If this function does cause a status change to occur then
- * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
- * </p>
- *
- * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
- * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
- * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
- * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+ * Should only be called by {@link UsbPort#setRoles}.
*
* @hide
*/
- @UnsupportedAppUsage
- public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
- Preconditions.checkNotNull(port, "port must not be null");
- UsbPort.checkRoles(powerRole, dataRole);
-
+ void setPortRoles(UsbPort port, int powerRole, int dataRole) {
Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName());
try {
mService.setPortRoles(port.getId(), powerRole, dataRole);
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index afdb202..37154e4 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,104 +16,53 @@
package android.hardware.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.hardware.usb.V1_0.Constants;
-import android.os.Parcel;
-import android.os.Parcelable;
import com.android.internal.util.Preconditions;
/**
* Represents a physical USB port and describes its characteristics.
- * <p>
- * This object is immutable.
- * </p>
*
* @hide
*/
-public final class UsbPort implements Parcelable {
+@SystemApi
+public final class UsbPort {
private final String mId;
private final int mSupportedModes;
-
- public static final int MODE_NONE = Constants.PortMode.NONE;
- /**
- * Mode bit: This USB port can act as a downstream facing port (host).
- * <p>
- * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
- * combination of roles (and possibly others as well).
- * </p>
- */
- public static final int MODE_DFP = Constants.PortMode.DFP;
-
- /**
- * Mode bit: This USB port can act as an upstream facing port (device).
- * <p>
- * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
- * combination of roles (and possibly others as well).
- * </p>
- */
- public static final int MODE_UFP = Constants.PortMode.UFP;
-
- /**
- * Mode bit: This USB port can act either as an downstream facing port (host) or as
- * an upstream facing port (device).
- * <p>
- * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
- * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
- * combination of roles (and possibly others as well).
- * </p>
- */
- public static final int MODE_DUAL = Constants.PortMode.DRP;
-
- /**
- * Mode bit: This USB port can support USB Type-C Audio accessory.
- */
- public static final int MODE_AUDIO_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
-
- /**
- * Mode bit: This USB port can support USB Type-C debug accessory.
- */
- public static final int MODE_DEBUG_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
-
- /**
- * Power role: This USB port does not have a power role.
- */
- public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
-
- /**
- * Power role: This USB port can act as a source (provide power).
- */
- public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
-
- /**
- * Power role: This USB port can act as a sink (receive power).
- */
- public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
-
- /**
- * Power role: This USB port does not have a data role.
- */
- public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
-
- /**
- * Data role: This USB port can act as a host (access data services).
- */
- public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
-
- /**
- * Data role: This USB port can act as a device (offer data services).
- */
- public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+ private final UsbManager mUsbManager;
private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
+
/**
* Points to the first power role in the IUsb HAL.
*/
private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
/** @hide */
- public UsbPort(String id, int supportedModes) {
+ public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes) {
+ Preconditions.checkNotNull(id);
+ Preconditions.checkFlagsArgument(supportedModes,
+ MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY);
+
+ mUsbManager = usbManager;
mId = id;
mSupportedModes = supportedModes;
}
@@ -122,6 +71,8 @@
* Gets the unique id of the port.
*
* @return The unique id of the port; not intended for display.
+ *
+ * @hide
*/
public String getId() {
return mId;
@@ -133,23 +84,62 @@
* The actual mode of the port may vary depending on what is plugged into it.
* </p>
*
- * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
- * {@link #MODE_DUAL}.
+ * @return The supported modes: one of {@link UsbPortStatus#MODE_DFP},
+ * {@link UsbPortStatus#MODE_UFP}, or {@link UsbPortStatus#MODE_DUAL}.
+ *
+ * @hide
*/
public int getSupportedModes() {
return mSupportedModes;
}
/**
+ * Gets the status of this USB port.
+ *
+ * @return The status of the this port, or {@code null} if port is unknown.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @Nullable UsbPortStatus getStatus() {
+ return mUsbManager.getPortStatus(this);
+ }
+
+ /**
+ * Sets the desired role combination of the port.
+ * <p>
+ * The supported role combinations depend on what is connected to the port and may be
+ * determined by consulting
+ * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
+ * </p><p>
+ * Note: This function is asynchronous and may fail silently without applying
+ * the requested changes. If this function does cause a status change to occur then
+ * a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
+ * </p>
+ *
+ * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE} or
+ * {@link UsbPortStatus#POWER_ROLE_SINK}, or
+ * {@link UsbPortStatus#POWER_ROLE_NONE} if no power role.
+ * @param dataRole The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST} or
+ * {@link UsbPortStatus#DATA_ROLE_DEVICE}, or
+ * {@link UsbPortStatus#DATA_ROLE_NONE} if no data role.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public void setRoles(@UsbPortStatus.UsbPowerRole int powerRole,
+ @UsbPortStatus.UsbDataRole int dataRole) {
+ UsbPort.checkRoles(powerRole, dataRole);
+
+ mUsbManager.setPortRoles(this, powerRole, dataRole);
+ }
+
+ /**
* Combines one power and one data role together into a unique value with
* exactly one bit set. This can be used to efficiently determine whether
* a combination of roles is supported by testing whether that bit is present
* in a bit-field.
*
- * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
- * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
- * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
- * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+ * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE}
+ * or {@link UsbPortStatus#POWER_ROLE_SINK}, or 0 if no power role.
+ * @param dataRole The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST}
+ * or {@link UsbPortStatus#DATA_ROLE_DEVICE}, or 0 if no data role.
* @hide
*/
public static int combineRolesAsBit(int powerRole, int dataRole) {
@@ -276,30 +266,4 @@
public String toString() {
return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
}
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mId);
- dest.writeInt(mSupportedModes);
- }
-
- public static final Parcelable.Creator<UsbPort> CREATOR =
- new Parcelable.Creator<UsbPort>() {
- @Override
- public UsbPort createFromParcel(Parcel in) {
- String id = in.readString();
- int supportedModes = in.readInt();
- return new UsbPort(id, supportedModes);
- }
-
- @Override
- public UsbPort[] newArray(int size) {
- return new UsbPort[size];
- }
- };
}
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 2cd8209..d30201a 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -16,27 +16,134 @@
package android.hardware.usb;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.hardware.usb.V1_0.Constants;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Describes the status of a USB port.
- * <p>
- * This object is immutable.
- * </p>
*
* @hide
*/
+@Immutable
+@SystemApi
public final class UsbPortStatus implements Parcelable {
private final int mCurrentMode;
- private final int mCurrentPowerRole;
- private final int mCurrentDataRole;
+ private final @UsbPowerRole int mCurrentPowerRole;
+ private final @UsbDataRole int mCurrentDataRole;
private final int mSupportedRoleCombinations;
+ /**
+ * Power role: This USB port does not have a power role.
+ */
+ public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+
+ /**
+ * Power role: This USB port can act as a source (provide power).
+ */
+ public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+
+ /**
+ * Power role: This USB port can act as a sink (receive power).
+ */
+ public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+
+ @IntDef(prefix = { "POWER_ROLE_" }, value = {
+ POWER_ROLE_NONE,
+ POWER_ROLE_SOURCE,
+ POWER_ROLE_SINK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbPowerRole{}
+
+ /**
+ * Power role: This USB port does not have a data role.
+ */
+ public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+
+ /**
+ * Data role: This USB port can act as a host (access data services).
+ */
+ public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+
+ /**
+ * Data role: This USB port can act as a device (offer data services).
+ */
+ public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+
+ @IntDef(prefix = { "DATA_ROLE_" }, value = {
+ DATA_ROLE_NONE,
+ DATA_ROLE_HOST,
+ DATA_ROLE_DEVICE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbDataRole{}
+
+ /**
+ * There is currently nothing connected to this USB port.
+ */
+ public static final int MODE_NONE = Constants.PortMode.NONE;
+
+ /**
+ * This USB port can act as a downstream facing port (host).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+ * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+ */
+ public static final int MODE_DFP = Constants.PortMode.DFP;
+
+ /**
+ * This USB port can act as an upstream facing port (device).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
+ * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+ */
+ public static final int MODE_UFP = Constants.PortMode.UFP;
+
+ /**
+ * This USB port can act either as an downstream facing port (host) or as
+ * an upstream facing port (device).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+ * {@link #DATA_ROLE_HOST} combination of roles and the {@link #POWER_ROLE_SINK} and
+ * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+ *
+ * @hide
+ */
+ public static final int MODE_DUAL = Constants.PortMode.DRP;
+
+ /**
+ * This USB port can support USB Type-C Audio accessory.
+ */
+ public static final int MODE_AUDIO_ACCESSORY =
+ android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+
+ /**
+ * This USB port can support USB Type-C debug accessory.
+ */
+ public static final int MODE_DEBUG_ACCESSORY =
+ android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+
+ @IntDef(prefix = { "MODE_" }, flag = true, value = {
+ MODE_NONE,
+ MODE_DFP,
+ MODE_UFP,
+ MODE_AUDIO_ACCESSORY,
+ MODE_DEBUG_ACCESSORY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbPortMode{}
+
/** @hide */
- public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
- int supportedRoleCombinations) {
+ public UsbPortStatus(int currentMode, @UsbPowerRole int currentPowerRole,
+ @UsbDataRole int currentDataRole, int supportedRoleCombinations) {
mCurrentMode = currentMode;
mCurrentPowerRole = currentPowerRole;
mCurrentDataRole = currentDataRole;
@@ -46,9 +153,8 @@
/**
* Returns true if there is anything connected to the port.
*
- * @return True if there is anything connected to the port.
+ * @return {@code true} iff there is anything connected to the port.
*/
- @UnsupportedAppUsage
public boolean isConnected() {
return mCurrentMode != 0;
}
@@ -56,33 +162,31 @@
/**
* Gets the current mode of the port.
*
- * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
- * or 0 if nothing is connected.
+ * @return The current mode: {@link #MODE_DFP}, {@link #MODE_UFP},
+ * {@link #MODE_AUDIO_ACCESSORY}, {@link #MODE_DEBUG_ACCESSORY}, or {@link {@link #MODE_NONE} if
+ * nothing is connected.
*/
- @UnsupportedAppUsage
- public int getCurrentMode() {
+ public @UsbPortMode int getCurrentMode() {
return mCurrentMode;
}
/**
* Gets the current power role of the port.
*
- * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
- * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
+ * @return The current power role: {@link #POWER_ROLE_SOURCE}, {@link #POWER_ROLE_SINK}, or
+ * {@link #POWER_ROLE_NONE} if nothing is connected.
*/
- @UnsupportedAppUsage
- public int getCurrentPowerRole() {
+ public @UsbPowerRole int getCurrentPowerRole() {
return mCurrentPowerRole;
}
/**
* Gets the current data role of the port.
*
- * @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
- * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
+ * @return The current data role: {@link #DATA_ROLE_HOST}, {@link #DATA_ROLE_DEVICE}, or
+ * {@link #DATA_ROLE_NONE} if nothing is connected.
*/
- @UnsupportedAppUsage
- public int getCurrentDataRole() {
+ public @UsbDataRole int getCurrentDataRole() {
return mCurrentDataRole;
}
@@ -90,19 +194,20 @@
* Returns true if the specified power and data role combination is supported
* given what is currently connected to the port.
*
- * @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE}
- * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
- * @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST}
- * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+ * @param powerRole The power role to check: {@link #POWER_ROLE_SOURCE} or
+ * {@link #POWER_ROLE_SINK}, or {@link #POWER_ROLE_NONE} if no power role.
+ * @param dataRole The data role to check: either {@link #DATA_ROLE_HOST} or
+ * {@link #DATA_ROLE_DEVICE}, or {@link #DATA_ROLE_NONE} if no data role.
*/
- @UnsupportedAppUsage
- public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
+ public boolean isRoleCombinationSupported(@UsbPowerRole int powerRole,
+ @UsbDataRole int dataRole) {
return (mSupportedRoleCombinations &
UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * Get the supported role combinations.
+ */
public int getSupportedRoleCombinations() {
return mSupportedRoleCombinations;
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 4714587..49c3dc6 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -187,13 +187,19 @@
* is for a network to which the connectivity manager was failing over
* following a disconnect on another network.
* Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ *
+ * @deprecated See {@link NetworkInfo}.
*/
+ @Deprecated
public static final String EXTRA_IS_FAILOVER = "isFailover";
/**
* The lookup key for a {@link NetworkInfo} object. This is supplied when
* there is another network that it may be possible to connect to. Retrieve with
* {@link android.content.Intent#getParcelableExtra(String)}.
+ *
+ * @deprecated See {@link NetworkInfo}.
*/
+ @Deprecated
public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
/**
* The lookup key for a boolean that indicates whether there is a
@@ -214,7 +220,10 @@
* may be passed up from the lower networking layers, and its
* meaning may be specific to a particular network type. Retrieve
* it with {@link android.content.Intent#getStringExtra(String)}.
+ *
+ * @deprecated See {@link NetworkInfo#getExtraInfo()}.
*/
+ @Deprecated
public static final String EXTRA_EXTRA_INFO = "extraInfo";
/**
* The lookup key for an int that provides information about
@@ -895,7 +904,9 @@
*
* @return a {@link NetworkInfo} object for the current default network
* or {@code null} if no default network is currently active
+ * @deprecated See {@link NetworkInfo}.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public NetworkInfo getActiveNetworkInfo() {
try {
@@ -1079,7 +1090,9 @@
* @return a {@link NetworkInfo} object for the requested
* network or {@code null} if the {@code Network}
* is not valid.
+ * @deprecated See {@link NetworkInfo}.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public NetworkInfo getNetworkInfo(Network network) {
return getNetworkInfoForUid(network, Process.myUid(), false);
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 1b9a66c..80517ce 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -161,7 +162,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public LinkProperties() {
}
@@ -195,7 +196,7 @@
* @param iface The name of the network interface used for this link.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setInterfaceName(String iface) {
mIfaceName = iface;
ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size());
@@ -346,7 +347,7 @@
* object.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setLinkAddresses(Collection<LinkAddress> addresses) {
mLinkAddresses.clear();
for (LinkAddress address: addresses) {
@@ -392,7 +393,7 @@
* @param dnsServers The {@link Collection} of DNS servers to set in this object.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setDnsServers(Collection<InetAddress> dnsServers) {
mDnses.clear();
for (InetAddress dnsServer: dnsServers) {
@@ -529,7 +530,7 @@
* domains to search when resolving host names on this link.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setDomains(String domains) {
mDomains = domains;
}
@@ -552,7 +553,7 @@
* @param mtu The MTU to use for this link.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setMtu(int mtu) {
mMtu = mtu;
}
@@ -562,9 +563,7 @@
* this will return 0.
*
* @return The mtu value set for this link.
- * @hide
*/
- @UnsupportedAppUsage
public int getMtu() {
return mMtu;
}
@@ -613,7 +612,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public boolean addRoute(RouteInfo route) {
if (route != null) {
String routeIface = route.getInterface();
@@ -688,7 +687,7 @@
* @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void setHttpProxy(ProxyInfo proxy) {
mHttpProxy = proxy;
}
@@ -760,7 +759,7 @@
* Clears this object to its initial state.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void clear() {
mIfaceName = null;
mLinkAddresses.clear();
diff --git a/core/java/android/net/NetworkBadging.java b/core/java/android/net/NetworkBadging.java
index c119b63..cd9ea88 100644
--- a/core/java/android/net/NetworkBadging.java
+++ b/core/java/android/net/NetworkBadging.java
@@ -21,13 +21,10 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.net.wifi.WifiManager;
-import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -96,34 +93,4 @@
throw new IllegalArgumentException("Invalid signal level: " + signalLevel);
}
}
-
- /**
- * Returns the badged wifi signal resource id for the given signal level.
- *
- * <p>This badged wifi signal resource should be displayed with the quality badge retrieved
- * from {@link #getWifiBadgeResource(int)}. If there is no badge,
- * {@link #getWifiBadgeResource(int)} should be used instead of this method.
- *
- * @param signalLevel The level returned by {@link WifiManager#calculateSignalLevel(int, int)}
- * for a network. Must be between 0 and {@link WifiManager#RSSI_LEVELS}-1.
- * @return the @DrawableRes for the icon
- * @throws IllegalArgumentException for an invalid signal level
- * @hide
- */
- @DrawableRes private static int getBadgedWifiSignalResource(int signalLevel) {
- switch (signalLevel) {
- case 0:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_0_bars;
- case 1:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_1_bar;
- case 2:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_2_bars;
- case 3:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_3_bars;
- case 4:
- return com.android.internal.R.drawable.ic_signal_wifi_badged_4_bars;
- default:
- throw new IllegalArgumentException("Invalid signal level: " + signalLevel);
- }
- }
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8a5f43d..1b44c92 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1017,7 +1017,7 @@
* @return The bearer-specific signal strength.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public int getSignalStrength() {
return mSignalStrength;
}
@@ -1467,7 +1467,7 @@
appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
NetworkCapabilities::capabilityNameOf, "&");
}
- if (0 != mNetworkCapabilities) {
+ if (0 != mUnwantedNetworkCapabilities) {
sb.append(" Unwanted: ");
appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
NetworkCapabilities::capabilityNameOf, "&");
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 1a1d2d334..89d9961 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -28,7 +28,20 @@
* Describes the status of a network interface.
* <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents
* the current network connection.
+ *
+ * @deprecated Callers should instead use the {@link ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes, or switch to use
+ * {@link ConnectivityManager#getNetworkCapabilities} or
+ * {@link ConnectivityManager#getLinkProperties} to get information synchronously. Keep
+ * in mind that while callbacks are guaranteed to be called for every event in order,
+ * synchronous calls have no such constraints, and as such it is unadvisable to use the
+ * synchronous methods inside the callbacks as they will often not offer a view of
+ * networking that is consistent (that is: they may return a past or a future state with
+ * respect to the event being processed by the callback). Instead, callers are advised
+ * to only use the arguments of the callbacks, possibly memorizing the specific bits of
+ * information they need to keep from one callback to another.
*/
+@Deprecated
public class NetworkInfo implements Parcelable {
/**
@@ -52,7 +65,10 @@
* <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
* <tr><td><code>BLOCKED</code></td><td><code>DISCONNECTED</code></td></tr>
* </table>
+ *
+ * @deprecated See {@link NetworkInfo}.
*/
+ @Deprecated
public enum State {
CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN
}
@@ -61,7 +77,10 @@
* The fine-grained state of a network connection. This level of detail
* is probably of interest to few applications. Most should use
* {@link android.net.NetworkInfo.State State} instead.
+ *
+ * @deprecated See {@link NetworkInfo}.
*/
+ @Deprecated
public enum DetailedState {
/** Ready to start data connection setup. */
IDLE,
@@ -463,8 +482,10 @@
* Set the extraInfo field.
* @param extraInfo an optional {@code String} providing addditional network state
* information passed up from the lower networking layers.
+ * @deprecated See {@link NetworkInfo#getExtraInfo}.
* @hide
*/
+ @Deprecated
public void setExtraInfo(String extraInfo) {
synchronized (this) {
this.mExtraInfo = extraInfo;
@@ -488,7 +509,10 @@
* Report the extra information about the network state, if any was
* provided by the lower networking layers.
* @return the extra information, or null if not available
+ * @deprecated Use other services e.g. WifiManager to get additional information passed up from
+ * the lower networking layers.
*/
+ @Deprecated
public String getExtraInfo() {
synchronized (this) {
return mExtraInfo;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 04b6b44..3b01b03 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.net.NetworkCapabilities.NetCapability;
import android.net.NetworkCapabilities.Transport;
@@ -344,7 +345,7 @@
* @param signalStrength the bearer-specific signal strength.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public Builder setSignalStrength(int signalStrength) {
mNetworkCapabilities.setSignalStrength(signalStrength);
return this;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 124d7b1..f3810bd 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -17,8 +17,10 @@
package android.os;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.opengl.EGL14;
@@ -57,9 +59,9 @@
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
- private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
+ private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
private ClassLoader mClassLoader;
private String mLayerPath;
@@ -255,8 +257,7 @@
return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
}
// Make sure we have good settings to use
- if (globalSettingsDriverPkgs.isEmpty() || globalSettingsDriverValues.isEmpty()
- || (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size())) {
+ if (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size()) {
Log.w(TAG,
"Global.Settings values are invalid: "
+ "globalSettingsDriverPkgs.size = "
@@ -276,9 +277,141 @@
}
/**
+ * Get the ANGLE package name.
+ */
+ private String getAnglePackageName(Context context) {
+ Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID);
+
+ List<ResolveInfo> resolveInfos = context.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
+ if (resolveInfos.size() != 1) {
+ Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ + resolveInfos.size());
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ }
+ return "";
+ }
+
+ // Must be exactly 1 ANGLE PKG found to get here.
+ return resolveInfos.get(0).activityInfo.packageName;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a temporary rules file.
+ * True: Temporary rules file was loaded.
+ * False: Temporary rules file was *not* loaded.
+ */
+ private boolean setupAngleWithTempRulesFile(Context context,
+ String packageName,
+ String paths,
+ String devOptIn) {
+ // Check for temporary rules if debuggable or root
+ if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) {
+ Log.v(TAG, "Skipping loading temporary rules file");
+ return false;
+ }
+
+ String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
+
+ if ((angleTempRules == null) || angleTempRules.isEmpty()) {
+ Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
+ return false;
+ }
+
+ Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
+
+ File tempRulesFile = new File(angleTempRules);
+ if (tempRulesFile.exists()) {
+ Log.i(TAG, angleTempRules + " exists, loading file.");
+ try {
+ FileInputStream stream = new FileInputStream(angleTempRules);
+
+ try {
+ FileDescriptor rulesFd = stream.getFD();
+ long rulesOffset = 0;
+ long rulesLength = stream.getChannel().size();
+ Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
+
+ setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+
+ stream.close();
+
+ // We successfully setup ANGLE, so return with good status
+ return true;
+ } catch (IOException e) {
+ Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
+ }
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Temp ANGLE rules file not found: " + e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
+ * True: Rules file was loaded.
+ * False: Rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
+ // b/121153494
+ // Skip APK rules file checking.
+ if (!DEBUG) {
+ Log.v(TAG, "Skipping loading the rules file.");
+ // Fill in some default values for now, so the loader can get an answer when it asks.
+ // Most importantly, we need to indicate which app we are init'ing and what the
+ // developer options for it are so we can turn on ANGLE if needed.
+ setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
+ * True: APK rules file was loaded.
+ * False: APK rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesApk(String anglePkgName,
+ ApplicationInfo angleInfo,
+ Context context,
+ String packageName,
+ String paths,
+ String devOptIn) {
+ // Pass the rules file to loader for ANGLE decisions
+ try {
+ AssetManager angleAssets =
+ context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+
+ try {
+ AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+
+ setAngleInfo(paths, packageName, devOptIn, assetsFd.getFileDescriptor(),
+ assetsFd.getStartOffset(), assetsFd.getLength());
+
+ assetsFd.close();
+
+ return true;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
+ + " from '" + anglePkgName + "': " + e);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
+ }
+
+ return false;
+ }
+
+ /**
* Pass ANGLE details down to trigger enable logic
*/
- private void setupAngle(Context context, Bundle bundle, String packageName) {
+ public void setupAngle(Context context, Bundle bundle, String packageName) {
String devOptIn = getDriverForPkg(bundle, packageName);
if (DEBUG) {
@@ -286,98 +419,47 @@
+ "set to: '" + devOptIn + "'");
}
+ String anglePkgName = getAnglePackageName(context);
+ if (anglePkgName.isEmpty()) {
+ Log.e(TAG, "Failed to find ANGLE package.");
+ return;
+ }
+
ApplicationInfo angleInfo;
try {
- angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
+ angleInfo = context.getPackageManager().getApplicationInfo(anglePkgName,
PackageManager.MATCH_SYSTEM_ONLY);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
+ Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
return;
}
String abi = chooseAbi(angleInfo);
// Build a path that includes installed native libs and APK
- StringBuilder sb = new StringBuilder();
- sb.append(angleInfo.nativeLibraryDir)
- .append(File.pathSeparator)
- .append(angleInfo.sourceDir)
- .append("!/lib/")
- .append(abi);
- String paths = sb.toString();
+ String paths = angleInfo.nativeLibraryDir
+ + File.pathSeparator
+ + angleInfo.sourceDir
+ + "!/lib/"
+ + abi;
if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
- // Look up rules file to pass to ANGLE
- FileDescriptor rulesFd = null;
- long rulesOffset = 0;
- long rulesLength = 0;
-
- // Check for temporary rules if debuggable or root
- if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
- String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
- if (angleTempRules != null && !angleTempRules.isEmpty()) {
- Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
- File tempRulesFile = new File(angleTempRules);
- if (tempRulesFile.exists()) {
- Log.i(TAG, angleTempRules + " exists, loading file.");
- FileInputStream stream = null;
- try {
- stream = new FileInputStream(angleTempRules);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Unable to create stream for temp ANGLE rules");
- }
-
- if (stream != null) {
- try {
- rulesFd = stream.getFD();
- rulesOffset = 0;
- rulesLength = stream.getChannel().size();
- Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
- } catch (IOException e) {
- Log.w(TAG, "Failed to get input stream for " + angleTempRules);
- }
- }
- }
- }
+ if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
+ // We setup ANGLE with a temp rules file, so we're done here.
+ return;
}
- // If no temp rules, load the real ones from the APK
- if (DEBUG && (rulesFd == null)) {
-
- // Pass the rules file to loader for ANGLE decisions
- AssetManager angleAssets = null;
- try {
- angleAssets =
- context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
- return;
- }
-
- AssetFileDescriptor assetsFd = null;
- try {
- assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
- } catch (IOException e) {
- Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
- + "'" + ANGLE_PACKAGE_NAME + "'");
- return;
- }
-
- if (assetsFd != null) {
- rulesFd = assetsFd.getFileDescriptor();
- rulesOffset = assetsFd.getStartOffset();
- rulesLength = assetsFd.getLength();
- } else {
- Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
- return;
- }
+ // b/121153494
+ if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
+ // We setup ANGLE with defaults, so we're done here.
+ return;
}
- // Further opt-in logic is handled in native, so pass relevant info down
- // TODO: Move the ANGLE selection logic earlier so we don't need to keep these
- // file descriptors open.
- setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+ if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
+ // We setup ANGLE with rules from the APK, so we're done here.
+ return;
+ }
}
/**
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 8a03e9e..df1a713 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,6 +16,7 @@
package android.os.storage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -348,6 +349,32 @@
return intent;
}
+ /**
+ * Builds an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to allow the user to grant access to any
+ * directory subtree (or entire volume) from the {@link android.provider.DocumentsProvider}s
+ * available on the device. The initial location of the document navigation will be the root of
+ * this {@link StorageVolume}.
+ *
+ * Note that the returned {@link Intent} simply suggests that the user picks this {@link
+ * StorageVolume} by default, but the user may select a different location. Callers must respect
+ * the user's chosen location, even if it is different from the originally requested location.
+ *
+ * @return intent to {@link Intent#ACTION_OPEN_DOCUMENT_TREE} initially showing the contents
+ * of this {@link StorageVolume}
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+ */
+ @NonNull public Intent createOpenDocumentTreeIntent() {
+ final String rootId = isEmulated()
+ ? DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID
+ : mFsUuid;
+ final Uri rootUri = DocumentsContract.buildRootUri(
+ DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId);
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
+ .putExtra(DocumentsContract.EXTRA_INITIAL_URI, rootUri)
+ .putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+ return intent;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof StorageVolume && mPath != null) {
diff --git a/core/java/android/permission/RuntimePermissionPresenter.java b/core/java/android/permission/RuntimePermissionPresenter.java
index 3ce3c9d..c607e3f 100644
--- a/core/java/android/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/permission/RuntimePermissionPresenter.java
@@ -16,27 +16,28 @@
package android.permission;
+import static android.permission.RuntimePermissionPresenterService.SERVICE_INTERFACE;
+
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+import com.android.internal.infra.AbstractRemoteService;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -80,7 +81,7 @@
*/
public interface OnCountPermissionAppsResultCallback {
/**
- * The result for {@link #countPermissionApps(List, boolean,
+ * The result for {@link #countPermissionApps(List, boolean, boolean,
* OnCountPermissionAppsResultCallback, Handler)}.
*
* @param numApps The number of apps that have one of the permissions
@@ -110,8 +111,13 @@
}
}
- private RuntimePermissionPresenter(Context context) {
- mRemoteService = new RemoteService(context);
+ private RuntimePermissionPresenter(@NonNull Context context) {
+ Intent intent = new Intent(SERVICE_INTERFACE);
+ intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
+ ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+
+ mRemoteService = new RemoteService(context,
+ serviceInfo.getComponentInfo().getComponentName());
}
/**
@@ -126,8 +132,8 @@
checkNotNull(packageName);
checkNotNull(callback);
- mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
- mRemoteService, packageName, callback, handler));
+ mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
+ packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
}
/**
@@ -141,8 +147,8 @@
checkNotNull(packageName);
checkNotNull(permissionName);
- mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
- mRemoteService, packageName, permissionName));
+ mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
+ permissionName));
}
/**
@@ -160,183 +166,192 @@
checkCollectionElementsNotNull(permissionNames, "permissionNames");
checkNotNull(callback);
- mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
- mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
+ mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
+ permissionNames, countOnlyGranted, countSystem, callback,
+ handler == null ? mRemoteService.getHandler() : handler));
}
- private static final class RemoteService
- extends Handler implements ServiceConnection {
+ /**
+ * A connection to the remote service
+ */
+ static final class RemoteService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteService,
+ IRuntimePermissionPresenter> {
private static final long UNBIND_TIMEOUT_MILLIS = 10000;
+ private static final long MESSAGE_TIMEOUT_MILLIS = 30000;
- public static final int MSG_UNBIND = 0;
-
- private final Object mLock = new Object();
-
- private final Context mContext;
-
- @GuardedBy("mLock")
- private final List<Message> mPendingWork = new ArrayList<>();
-
- @GuardedBy("mLock")
- private IRuntimePermissionPresenter mRemoteInstance;
-
- @GuardedBy("mLock")
- private boolean mBound;
-
- RemoteService(Context context) {
- super(context.getMainLooper(), null, false);
- mContext = context;
+ /**
+ * Create a connection to the remote service
+ *
+ * @param context A context to use
+ * @param componentName The component of the service to connect to
+ */
+ RemoteService(@NonNull Context context, @NonNull ComponentName componentName) {
+ super(context, SERVICE_INTERFACE, componentName, UserHandle.myUserId(),
+ service -> Log.e(TAG, "RuntimePermPresenterService " + service + " died"),
+ false, false, 1);
}
- public void processMessage(Message message) {
- synchronized (mLock) {
- if (!mBound) {
- Intent intent = new Intent(
- RuntimePermissionPresenterService.SERVICE_INTERFACE);
- intent.setPackage(mContext.getPackageManager()
- .getPermissionControllerPackageName());
- mBound = mContext.bindService(intent, this,
- Context.BIND_AUTO_CREATE);
+ /**
+ * @return The default handler used by this service.
+ */
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ protected @NonNull IRuntimePermissionPresenter getServiceInterface(
+ @NonNull IBinder binder) {
+ return IRuntimePermissionPresenter.Stub.asInterface(binder);
+ }
+
+ @Override
+ protected long getTimeoutIdleBindMillis() {
+ return UNBIND_TIMEOUT_MILLIS;
+ }
+
+ @Override
+ protected long getRemoteRequestMillis() {
+ return MESSAGE_TIMEOUT_MILLIS;
+ }
+
+ @Override
+ public void scheduleRequest(@NonNull PendingRequest<RemoteService,
+ IRuntimePermissionPresenter> pendingRequest) {
+ super.scheduleRequest(pendingRequest);
+ }
+
+ @Override
+ public void scheduleAsyncRequest(
+ @NonNull AsyncRequest<IRuntimePermissionPresenter> request) {
+ super.scheduleAsyncRequest(request);
+ }
+ }
+
+ /**
+ * Request for {@link #getAppPermissions}
+ */
+ private static final class PendingGetAppPermissionRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+ private final @NonNull String mPackageName;
+ private final @NonNull OnGetAppPermissionResultCallback mCallback;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingGetAppPermissionRequest(@NonNull RemoteService service,
+ @NonNull String packageName, @NonNull OnGetAppPermissionResultCallback callback,
+ @NonNull Handler handler) {
+ super(service);
+
+ mPackageName = packageName;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> {
+ final List<RuntimePermissionPresentationInfo> reportedPermissions;
+ List<RuntimePermissionPresentationInfo> permissions = null;
+ if (result != null) {
+ permissions = result.getParcelableArrayList(KEY_RESULT);
}
- mPendingWork.add(message);
- scheduleNextMessageIfNeededLocked();
- }
+ if (permissions == null) {
+ permissions = Collections.emptyList();
+ }
+ reportedPermissions = permissions;
+
+ callback.onGetAppPermissions(reportedPermissions);
+
+ finish();
+ }, handler);
}
@Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
- scheduleNextMessageIfNeededLocked();
- }
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onGetAppPermissions(Collections.emptyList());
}
@Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mLock) {
- mRemoteInstance = null;
- }
- }
-
- private void getAppPermissions(@NonNull String packageName,
- @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
+ public void run() {
try {
- remoteInstance.getAppPermissions(packageName,
- new RemoteCallback(result -> {
- final List<RuntimePermissionPresentationInfo> reportedPermissions;
- List<RuntimePermissionPresentationInfo> permissions = null;
- if (result != null) {
- permissions = result.getParcelableArrayList(KEY_RESULT);
- }
- if (permissions == null) {
- permissions = Collections.emptyList();
- }
- reportedPermissions = permissions;
- if (handler != null) {
- handler.post(
- () -> callback.onGetAppPermissions(reportedPermissions));
- } else {
- callback.onGetAppPermissions(reportedPermissions);
- }
- }, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
- scheduleUnbind();
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
+ getService().getServiceInterface().getAppPermissions(mPackageName, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting app permission", e);
}
}
+ }
- private void revokeAppPermissions(@NonNull String packageName,
+ /**
+ * Request for {@link #revokeRuntimePermission}
+ */
+ private static final class PendingRevokeAppPermissionRequest
+ implements AbstractRemoteService.AsyncRequest<IRuntimePermissionPresenter> {
+ private final @NonNull String mPackageName;
+ private final @NonNull String mPermissionName;
+
+ private PendingRevokeAppPermissionRequest(@NonNull String packageName,
@NonNull String permissionName) {
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
- try {
- remoteInstance.revokeRuntimePermission(packageName, permissionName);
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
- }
+ mPackageName = packageName;
+ mPermissionName = permissionName;
}
- private void countPermissionApps(@NonNull List<String> permissionNames,
- boolean countOnlyGranted, boolean countSystem,
- @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
- final IRuntimePermissionPresenter remoteInstance;
-
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
-
+ @Override
+ public void run(IRuntimePermissionPresenter remoteInterface) {
try {
- remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
- new RemoteCallback(result -> {
- final int numApps;
- if (result != null) {
- numApps = result.getInt(KEY_RESULT);
- } else {
- numApps = 0;
- }
-
- if (handler != null) {
- handler.post(() -> callback.onCountPermissionApps(numApps));
- } else {
- callback.onCountPermissionApps(numApps);
- }
- }, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error counting permission apps", re);
- }
-
- scheduleUnbind();
-
- synchronized (mLock) {
- scheduleNextMessageIfNeededLocked();
+ remoteInterface.revokeRuntimePermission(mPackageName, mPermissionName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error revoking app permission", e);
}
}
+ }
- private void unbind() {
- synchronized (mLock) {
- if (mBound) {
- mContext.unbindService(this);
- mBound = false;
+ /**
+ * Request for {@link #countPermissionApps}
+ */
+ private static final class PendingCountPermissionAppsRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+ private final @NonNull List<String> mPermissionNames;
+ private final @NonNull OnCountPermissionAppsResultCallback mCallback;
+ private final boolean mCountOnlyGranted;
+ private final boolean mCountSystem;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingCountPermissionAppsRequest(@NonNull RemoteService service,
+ @NonNull List<String> permissionNames, boolean countOnlyGranted,
+ boolean countSystem, @NonNull OnCountPermissionAppsResultCallback callback,
+ @NonNull Handler handler) {
+ super(service);
+
+ mPermissionNames = permissionNames;
+ mCountOnlyGranted = countOnlyGranted;
+ mCountSystem = countSystem;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> {
+ final int numApps;
+ if (result != null) {
+ numApps = result.getInt(KEY_RESULT);
+ } else {
+ numApps = 0;
}
- mRemoteInstance = null;
- }
+
+ callback.onCountPermissionApps(numApps);
+
+ finish();
+ }, handler);
}
- @GuardedBy("mLock")
- private void scheduleNextMessageIfNeededLocked() {
- if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
- Message nextMessage = mPendingWork.remove(0);
- sendMessage(nextMessage);
- }
+ @Override
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onCountPermissionApps(0);
}
- private void scheduleUnbind() {
- removeMessages(MSG_UNBIND);
- sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
- .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
+ @Override
+ public void run() {
+ try {
+ getService().getServiceInterface().countPermissionApps(mPermissionNames,
+ mCountOnlyGranted, mCountSystem, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error counting permission apps", e);
+ }
}
}
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index f01d5b1..015146466 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -123,7 +123,7 @@
mContext = context;
mAudioManager = context.getSystemService(AudioManager.class);
mNotificationManager = context.getSystemService(NotificationManager.class);
- mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+ mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_ALARMS) != 0;
mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
@@ -485,7 +485,7 @@
mZenMode = mNotificationManager.getZenMode();
updateSlider();
} else if (NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED.equals(action)) {
- mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+ mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_ALARMS) != 0;
mAllowMedia = (mNotificationPolicy.priorityCategories
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index cd991cc..a323ed1 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -238,6 +238,9 @@
"com.android.externalstorage.documents";
/** {@hide} */
+ public static final String EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID = "primary";
+
+ /** {@hide} */
public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
/**
@@ -857,16 +860,6 @@
}
/**
- * Builds URI for user home directory on external (local) storage.
- * {@hide}
- */
- public static Uri buildHomeUri() {
- // TODO: Avoid this type of interpackage copying. Added here to avoid
- // direct coupling, but not ideal.
- return DocumentsContract.buildRootUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY, "home");
- }
-
- /**
* Build URI representing the recently modified documents of a specific root
* in a document provider. When queried, a provider will return zero or more
* rows with columns defined by {@link Document}.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 299db73..d93985c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12820,6 +12820,17 @@
"privileged_device_identifier_3p_check_relaxed";
/**
+ * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+ * permission check for preloaded non-privileged apps.
+ *
+ * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+ *
+ * @hide
+ */
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED =
+ "privileged_device_identifier_non_priv_check_relaxed";
+
+ /**
* If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
* and restoring to lower version of platform API will be skipped.
*
@@ -12949,14 +12960,20 @@
/**
* Property used by {@code com.android.server.SystemServer} on start to decide whether
- * the Content Capture service should be created or not
+ * the Content Capture service should be created or not.
*
- * <p>By default it should *NOT* be set (in which case the decision is based on whether
- * the OEM provides an implementation for the service), but it can be overridden to:
+ * <p>Possible values are:
*
* <ul>
- * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency.
- * <li>Enable the CTS tests to be run on AOSP builds
+ * <li>If set to {@code default}, it will only be set if the OEM provides and defines the
+ * service name by overlaying {@code config_defaultContentCaptureService} (this is the
+ * "default" mode)
+ * <li>If set to {@code always}, it will always be enabled, even when the resource is not
+ * overlaid (this is useful during development and to run the CTS tests on AOSP builds).
+ * <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs
+ * can disable it remotely in case of emergency by setting to something else (like
+ * {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one
+ * of the values above).
* </ul>
*
* @hide
@@ -14260,6 +14277,44 @@
}
}
+ /**
+ * <p>
+ * A Settings panel is floating UI that contains a fixed subset of settings to address a
+ * particular user problem. For example, the
+ * {@link #ACTION_INTERNET_CONNECTIVITY Internet Panel} surfaces settings related to
+ * connecting to the internet.
+ * <p>
+ * Settings panels appear above the calling app to address the problem without
+ * the user needing to open Settings and thus leave their current screen.
+ */
+ public static final class Panel {
+ private Panel() {
+ }
+
+ /**
+ * Activity Action: Show a settings dialog containing settings to enable internet
+ * connection.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_INTERNET_CONNECTIVITY =
+ "android.settings.panel.action.INTERNET_CONNECTIVITY";
+
+ /**
+ * Activity Action: Show a settings dialog containing all volume streams.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VOLUME =
+ "android.settings.panel.action.VOLUME";
+ }
+
private static final String[] PM_WRITE_SETTINGS = {
android.Manifest.permission.WRITE_SETTINGS
};
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 27df845..bad7ddd 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -163,14 +163,15 @@
final int height = rect.bottom - rect.top;
final int width = rect.right - rect.left;
final WindowManager.LayoutParams windowParams = window.getAttributes();
- windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- windowParams.y = rect.top - height;
+ windowParams.gravity = Gravity.TOP | Gravity.LEFT;
+ windowParams.y = rect.top + height;
windowParams.height = height;
windowParams.x = rect.left;
windowParams.width = width;
window.setAttributes(windowParams);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index 028180d..784e719 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -17,26 +17,30 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.contentcapture.ContentCaptureEvent;
+import java.util.Arrays;
import java.util.List;
/**
- * Batch of content capture events.
+ * Not needed anymore...
+ *
+ * @deprecated
*
* @hide
*/
@SystemApi
+@Deprecated
public final class ContentCaptureEventsRequest implements Parcelable {
+// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore
- private final ParceledListSlice<ContentCaptureEvent> mEvents;
+ private final ContentCaptureEvent mEvent;
/** @hide */
- public ContentCaptureEventsRequest(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
- mEvents = events;
+ public ContentCaptureEventsRequest(@NonNull ContentCaptureEvent event) {
+ mEvent = event;
}
/**
@@ -44,7 +48,7 @@
*/
@NonNull
public List<ContentCaptureEvent> getEvents() {
- return mEvents.getList();
+ return Arrays.asList(mEvent);
}
@Override
@@ -54,7 +58,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mEvents, flags);
+ parcel.writeParcelable(mEvent, flags);
}
public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 953ccf1..c9d46dd 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -31,6 +31,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.service.autofill.AutofillService;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -40,6 +41,7 @@
import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
import android.view.contentcapture.IContentCaptureDirectManager;
+import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.os.IResultReceiver;
@@ -81,6 +83,12 @@
private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
@Override
+ public void onConnectedStateChanged(boolean state) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnectedStateChanged,
+ ContentCaptureService.this, state));
+ }
+
+ @Override
public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
IResultReceiver clientReceiver) {
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
@@ -108,10 +116,9 @@
new IContentCaptureDirectManager.Stub() {
@Override
- public void sendEvents(String sessionId,
- @SuppressWarnings("rawtypes") ParceledListSlice events) {
+ public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
- ContentCaptureService.this, sessionId, Binder.getCallingUid(), events));
+ ContentCaptureService.this, Binder.getCallingUid(), events));
}
};
@@ -204,6 +211,15 @@
}
/**
+ * Called when the Android system connects to service.
+ *
+ * <p>You should generally do initialization here rather than in {@link #onCreate}.
+ */
+ public void onConnected() {
+ Slog.i(TAG, "bound to " + getClass().getName());
+ }
+
+ /**
* Creates a new content capture session.
*
* @param context content capture context
@@ -217,18 +233,29 @@
}
/**
- * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
- * session.
*
- * @param sessionId the session's Id
- * @param request the events
+ * @deprecated use {@link #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)}
+ * instead.
*/
+ @Deprecated
public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
@NonNull ContentCaptureEventsRequest request) {
if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
}
/**
+ * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
+ * session.
+ *
+ * @param sessionId the session's Id
+ * @param event the event
+ */
+ public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId,
+ @NonNull ContentCaptureEvent event) {
+ if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+ onContentCaptureEventsRequest(sessionId, new ContentCaptureEventsRequest(event));
+ }
+ /**
* Notifies the service of {@link SnapshotData snapshot data} associated with a session.
*
* @param sessionId the session's Id
@@ -246,6 +273,15 @@
if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
}
+ /**
+ * Called when the Android system disconnects from the service.
+ *
+ * <p> At this point this service may no longer be an active {@link AutofillService}.
+ */
+ public void onDisconnected() {
+ Slog.i(TAG, "unbinding from " + getClass().getName());
+ }
+
@Override
@CallSuper
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -260,6 +296,14 @@
}
}
+ private void handleOnConnectedStateChanged(boolean state) {
+ if (state) {
+ onConnected();
+ } else {
+ onDisconnected();
+ }
+ }
+
//TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
// so we don't need to create a temporary InteractionSessionId for each event.
@@ -271,11 +315,37 @@
mClientInterface.asBinder());
}
- private void handleSendEvents(@NonNull String sessionId, int uid,
- @NonNull ParceledListSlice<ContentCaptureEvent> events) {
- if (handleIsRightCallerFor(sessionId, uid)) {
- onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId),
- new ContentCaptureEventsRequest(events));
+ private void handleSendEvents(int uid,
+ @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
+
+ // Most events belong to the same session, so we can keep a reference to the last one
+ // to avoid creating too many ContentCaptureSessionId objects
+ String lastSessionId = null;
+ ContentCaptureSessionId sessionId = null;
+
+ final List<ContentCaptureEvent> events = parceledEvents.getList();
+ for (int i = 0; i < events.size(); i++) {
+ final ContentCaptureEvent event = events.get(i);
+ if (!handleIsRightCallerFor(event, uid)) continue;
+ String sessionIdString = event.getSessionId();
+ if (!sessionIdString.equals(lastSessionId)) {
+ sessionId = new ContentCaptureSessionId(sessionIdString);
+ lastSessionId = sessionIdString;
+ }
+ switch (event.getType()) {
+ case ContentCaptureEvent.TYPE_SESSION_STARTED:
+ final ContentCaptureContext clientContext = event.getClientContext();
+ clientContext.setParentSessionId(event.getParentSessionId());
+ mSessionsByUid.put(sessionIdString, uid);
+ onCreateContentCaptureSession(clientContext, sessionId);
+ break;
+ case ContentCaptureEvent.TYPE_SESSION_FINISHED:
+ mSessionsByUid.remove(sessionIdString);
+ onDestroyContentCaptureSession(sessionId);
+ break;
+ default:
+ onContentCaptureEvent(sessionId, event);
+ }
}
}
@@ -290,9 +360,18 @@
}
/**
- * Checks if the given {@code uid} owns the session.
+ * Checks if the given {@code uid} owns the session associated with the event.
*/
- private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) {
+ private boolean handleIsRightCallerFor(@NonNull ContentCaptureEvent event, int uid) {
+ final String sessionId;
+ switch (event.getType()) {
+ case ContentCaptureEvent.TYPE_SESSION_STARTED:
+ case ContentCaptureEvent.TYPE_SESSION_FINISHED:
+ sessionId = event.getParentSessionId();
+ break;
+ default:
+ sessionId = event.getSessionId();
+ }
final Integer rightUid = mSessionsByUid.get(sessionId);
if (rightUid == null) {
if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
@@ -323,7 +402,7 @@
final Bundle extras;
if (binder != null) {
extras = new Bundle();
- extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
+ extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder);
} else {
extras = null;
}
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index 20e8e99..1b4cccf 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -29,6 +29,7 @@
* @hide
*/
oneway interface IContentCaptureService {
+ void onConnectedStateChanged(boolean state);
void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
in IResultReceiver clientReceiver);
void onSessionFinished(String sessionId);
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/service/euicc/DownloadSubscriptionResult.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/service/euicc/DownloadSubscriptionResult.aidl
index b7a7920..b625fd6 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/service/euicc/DownloadSubscriptionResult.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2015, The Android Open Source Project
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.hardware.usb;
+package android.service.euicc;
-parcelable UsbPort;
+parcelable DownloadSubscriptionResult;
diff --git a/core/java/android/service/euicc/DownloadSubscriptionResult.java b/core/java/android/service/euicc/DownloadSubscriptionResult.java
new file mode 100644
index 0000000..b410e35
--- /dev/null
+++ b/core/java/android/service/euicc/DownloadSubscriptionResult.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.euicc;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.euicc.EuiccService.ResolvableError;
+import android.service.euicc.EuiccService.Result;
+
+/**
+ * Result of a {@link EuiccService#onDownloadSubscription} operation.
+ * @hide
+ */
+@SystemApi
+public final class DownloadSubscriptionResult implements Parcelable {
+
+ public static final Creator<DownloadSubscriptionResult> CREATOR =
+ new Creator<DownloadSubscriptionResult>() {
+ @Override
+ public DownloadSubscriptionResult createFromParcel(Parcel in) {
+ return new DownloadSubscriptionResult(in);
+ }
+
+ @Override
+ public DownloadSubscriptionResult[] newArray(int size) {
+ return new DownloadSubscriptionResult[size];
+ }
+ };
+
+ private final @Result int mResult;
+ private final @ResolvableError int mResolvableErrors;
+ private final int mCardId;
+
+ public DownloadSubscriptionResult(@Result int result, @ResolvableError int resolvableErrors,
+ int cardId) {
+ this.mResult = result;
+ this.mResolvableErrors = resolvableErrors;
+ this.mCardId = cardId;
+ }
+
+ /** Gets the result of the operation. */
+ public @Result int getResult() {
+ return mResult;
+ }
+
+ /**
+ * Gets the bit map of resolvable errors.
+ *
+ * <p>The value is passed from EuiccService. The values can be
+ *
+ * <ul>
+ * <li>{@link EuiccService#RESOLVABLE_ERROR_CONFIRMATION_CODE}
+ * <li>{@link EuiccService#RESOLVABLE_ERROR_POLICY_RULES}
+ * </ul>
+ */
+ public @ResolvableError int getResolvableErrors() {
+ return mResolvableErrors;
+ }
+
+ /**
+ * Gets the card Id. This is used when resolving resolvable errors. The value is passed from
+ * EuiccService.
+ */
+ public int getCardId() {
+ return mCardId;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mResult);
+ dest.writeInt(mResolvableErrors);
+ dest.writeInt(mCardId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private DownloadSubscriptionResult(Parcel in) {
+ this.mResult = in.readInt();
+ this.mResolvableErrors = in.readInt();
+ this.mCardId = in.readInt();
+ }
+}
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 49a7320..4be1f9c 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -16,17 +16,24 @@
package android.service.euicc;
import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.telephony.TelephonyManager;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
import android.telephony.euicc.EuiccManager.OtaStatus;
import android.util.ArraySet;
+import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
@@ -73,6 +80,8 @@
*/
@SystemApi
public abstract class EuiccService extends Service {
+ private static final String TAG = "EuiccService";
+
/** Action which must be included in this service's intent filter. */
public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
@@ -115,30 +124,91 @@
public static final String ACTION_RESOLVE_NO_PRIVILEGES =
"android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
- /** Ask the user to input carrier confirmation code. */
+ /**
+ * Ask the user to input carrier confirmation code.
+ *
+ * @deprecated From Q, the resolvable errors happened in the download step are presented as
+ * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding action would be
+ * {@link #ACTION_RESOLVE_RESOLVABLE_ERRORS}.
+ */
+ @Deprecated
public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
"android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
+ /** Ask the user to resolve all the resolvable errors. */
+ public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS =
+ "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "RESOLVABLE_ERROR_" }, value = {
+ RESOLVABLE_ERROR_CONFIRMATION_CODE,
+ RESOLVABLE_ERROR_POLICY_RULES,
+ })
+ public @interface ResolvableError {}
+
+ /**
+ * Possible value for the bit map of resolvable errors indicating the download process needs
+ * the user to input confirmation code.
+ */
+ public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1 << 0;
+ /**
+ * Possible value for the bit map of resolvable errors indicating the download process needs
+ * the user's consent to allow profile policy rules.
+ */
+ public static final int RESOLVABLE_ERROR_POLICY_RULES = 1 << 1;
+
/**
* Intent extra set for resolution requests containing the package name of the calling app.
* This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM,
- * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE.
+ * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_RESOLVABLE_ERRORS.
*/
public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
"android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
/**
+ * Intent extra set for resolution requests containing the list of resolvable errors to be
+ * resolved. Each resolvable error is an integer. Its possible values include:
+ * <UL>
+ * <LI>{@link #RESOLVABLE_ERROR_CONFIRMATION_CODE}
+ * <LI>{@link #RESOLVABLE_ERROR_POLICY_RULES}
+ * </UL>
+ */
+ public static final String EXTRA_RESOLVABLE_ERRORS =
+ "android.service.euicc.extra.RESOLVABLE_ERRORS";
+
+ /**
* Intent extra set for resolution requests containing a boolean indicating whether to ask the
* user to retry another confirmation code.
*/
public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED =
"android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "RESULT_" }, value = {
+ RESULT_OK,
+ RESULT_MUST_DEACTIVATE_SIM,
+ RESULT_RESOLVABLE_ERRORS,
+ RESULT_NEED_CONFIRMATION_CODE,
+ RESULT_FIRST_USER,
+ })
+ public @interface Result {}
+
/** Result code for a successful operation. */
public static final int RESULT_OK = 0;
/** Result code indicating that an active SIM must be deactivated to perform the operation. */
public static final int RESULT_MUST_DEACTIVATE_SIM = -1;
- /** Result code indicating that the user must input a carrier confirmation code. */
+ /** Result code indicating that the user must resolve resolvable errors. */
+ public static final int RESULT_RESOLVABLE_ERRORS = -2;
+ /**
+ * Result code indicating that the user must input a carrier confirmation code.
+ *
+ * @deprecated From Q, the resolvable errors happened in the download step are presented as
+ * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding result would be
+ * {@link #RESULT_RESOLVABLE_ERRORS}.
+ */
+ @Deprecated
public static final int RESULT_NEED_CONFIRMATION_CODE = -2;
// New predefined codes should have negative values.
@@ -154,7 +224,7 @@
RESOLUTION_ACTIONS = new ArraySet<>();
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
- RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
+ RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS);
}
/**
@@ -169,6 +239,12 @@
*/
public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE =
"android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
+ /**
+ * String extra for resolution actions indicating whether the user allows policy rules.
+ * This is used and set by the implementation and used in {@code EuiccOperation}.
+ */
+ public static final String EXTRA_RESOLUTION_ALLOW_POLICY_RULES =
+ "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
private final IEuiccService.Stub mStubWrapper;
@@ -236,8 +312,7 @@
/**
* Return the EID of the eUICC.
*
- * @param slotId ID of the SIM slot being queried. This is currently not populated but is here
- * to future-proof the APIs.
+ * @param slotId ID of the SIM slot being queried.
* @return the EID.
* @see android.telephony.euicc.EuiccManager#getEid
*/
@@ -247,8 +322,7 @@
/**
* Return the status of OTA update.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @return The status of Euicc OTA update.
* @see android.telephony.euicc.EuiccManager#getOtaStatus
*/
@@ -257,8 +331,7 @@
/**
* Perform OTA if current OS is not the latest one.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param statusChangedCallback Function called when OTA status changed.
*/
public abstract void onStartOtaIfNecessary(
@@ -281,8 +354,7 @@
/**
* Return metadata for subscriptions which are available for download for this device.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
* eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
* should be returned to allow the user to consent to this operation first.
@@ -302,13 +374,44 @@
* @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
* eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
* should be returned to allow the user to consent to this operation first.
+ * @param resolvedBundle The bundle containing information on resolved errors. It can contain
+ * a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE},
+ * and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether
+ * the user allows profile policy rules or not.
+ * @return a DownloadSubscriptionResult instance including a result code, a resolvable errors
+ * bit map, and original the card Id. The result code may be one of the predefined
+ * {@code RESULT_} constants or any implementation-specific code starting with
+ * {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values
+ * defined in {@code RESOLVABLE_ERROR_}.
+ * @see android.telephony.euicc.EuiccManager#downloadSubscription
+ */
+ public abstract DownloadSubscriptionResult onDownloadSubscription(int slotId,
+ @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+ boolean forceDeactivateSim, @Nullable Bundle resolvedBundle);
+
+ /**
+ * Download the given subscription.
+ *
+ * @param slotId ID of the SIM slot to use for the operation.
+ * @param subscription The subscription to download.
+ * @param switchAfterDownload If true, the subscription should be enabled upon successful
+ * download.
+ * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
+ * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
+ * should be returned to allow the user to consent to this operation first.
* @return the result of the download operation. May be one of the predefined {@code RESULT_}
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
* @see android.telephony.euicc.EuiccManager#downloadSubscription
+ *
+ * @deprecated From Q, please use the above
+ * {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}.
*/
- public abstract int onDownloadSubscription(int slotId,
- DownloadableSubscription subscription, boolean switchAfterDownload,
- boolean forceDeactivateSim);
+ @Deprecated public @Result int onDownloadSubscription(int slotId,
+ @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+ boolean forceDeactivateSim) {
+ throw new UnsupportedOperationException("onDownloadSubscription(int, "
+ + "DownloadableSubscription, boolean, boolean) is deprecated.");
+ }
/**
* Return a list of all @link EuiccProfileInfo}s.
@@ -318,7 +421,7 @@
* @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
* @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
*/
- public abstract GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId);
+ public abstract @NonNull GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId);
/**
* Return info about the eUICC chip/device.
@@ -327,7 +430,7 @@
* @return the {@link EuiccInfo} for the eUICC chip/device.
* @see android.telephony.euicc.EuiccManager#getEuiccInfo
*/
- public abstract EuiccInfo onGetEuiccInfo(int slotId);
+ public abstract @NonNull EuiccInfo onGetEuiccInfo(int slotId);
/**
* Delete the given subscription.
@@ -341,7 +444,7 @@
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
* @see android.telephony.euicc.EuiccManager#deleteSubscription
*/
- public abstract int onDeleteSubscription(int slotId, String iccid);
+ public abstract @Result int onDeleteSubscription(int slotId, String iccid);
/**
* Switch to the given subscription.
@@ -357,7 +460,7 @@
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
* @see android.telephony.euicc.EuiccManager#switchToSubscription
*/
- public abstract int onSwitchToSubscription(int slotId, @Nullable String iccid,
+ public abstract @Result int onSwitchToSubscription(int slotId, @Nullable String iccid,
boolean forceDeactivateSim);
/**
@@ -379,8 +482,7 @@
* <p>This is intended to be used for device resets. As such, the reset should be performed even
* if an active SIM must be deactivated in order to access the eUICC.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @return the result of the erase operation. May be one of the predefined {@code RESULT_}
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
* @see android.telephony.euicc.EuiccManager#eraseSubscriptions
@@ -395,8 +497,7 @@
* should persist some bit that will remain accessible after the factory reset to bypass this
* flow when this method is called.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @return the result of the operation. May be one of the predefined {@code RESULT_} constants
* or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
*/
@@ -408,13 +509,26 @@
private class IEuiccServiceWrapper extends IEuiccService.Stub {
@Override
public void downloadSubscription(int slotId, DownloadableSubscription subscription,
- boolean switchAfterDownload, boolean forceDeactivateSim,
+ boolean switchAfterDownload, boolean forceDeactivateSim, Bundle resolvedBundle,
IDownloadSubscriptionCallback callback) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
- int result = EuiccService.this.onDownloadSubscription(
- slotId, subscription, switchAfterDownload, forceDeactivateSim);
+ DownloadSubscriptionResult result;
+ try {
+ result =
+ EuiccService.this.onDownloadSubscription(
+ slotId, subscription, switchAfterDownload, forceDeactivateSim,
+ resolvedBundle);
+ } catch (AbstractMethodError e) {
+ Log.w(TAG, "The new onDownloadSubscription(int, "
+ + "DownloadableSubscription, boolean, boolean, Bundle) is not "
+ + "implemented. Fall back to the old one.", e);
+ int resultCode = EuiccService.this.onDownloadSubscription(
+ slotId, subscription, switchAfterDownload, forceDeactivateSim);
+ result = new DownloadSubscriptionResult(resultCode,
+ 0 /* resolvableErrors */, TelephonyManager.INVALID_CARD_ID);
+ }
try {
callback.onComplete(result);
} catch (RemoteException e) {
diff --git a/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl b/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
index 6893c85..50ecbeb 100644
--- a/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
+++ b/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
@@ -16,7 +16,9 @@
package android.service.euicc;
+import android.service.euicc.DownloadSubscriptionResult;
+
/** @hide */
oneway interface IDownloadSubscriptionCallback {
- void onComplete(int result);
+ void onComplete(in DownloadSubscriptionResult result);
}
\ No newline at end of file
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index 45be527..c2cdf09 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -30,11 +30,12 @@
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
import android.telephony.euicc.DownloadableSubscription;
+import android.os.Bundle;
/** @hide */
oneway interface IEuiccService {
void downloadSubscription(int slotId, in DownloadableSubscription subscription,
- boolean switchAfterDownload, boolean forceDeactivateSim,
+ boolean switchAfterDownload, boolean forceDeactivateSim, in Bundle resolvedBundle,
in IDownloadSubscriptionCallback callback);
void getDownloadableSubscriptionMetadata(int slotId, in DownloadableSubscription subscription,
boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c850a4e..dd0242a 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -19,7 +19,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -179,13 +179,13 @@
* @param isExpanded whether the notification is expanded.
*/
public void onNotificationExpansionChanged(
- String key, boolean isUserAction, boolean isExpanded) {}
+ @NonNull String key, boolean isUserAction, boolean isExpanded) {}
/**
* Implement this to know when a direct reply is sent from a notification.
* @param key the notification key
*/
- public void onNotificationDirectReply(String key) {}
+ public void onNotificationDirectReply(@NonNull String key) {}
/**
* Implement this to know when a suggested reply is sent.
@@ -193,7 +193,9 @@
* @param reply the reply that is just sent
* @param source the source that provided the reply, e.g. SOURCE_FROM_APP
*/
- public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
+ public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
+ @Source int source) {
+ }
/**
* Implement this to know when an action is clicked.
@@ -201,7 +203,8 @@
* @param action the action that is just clicked
* @param source the source that provided the action, e.g. SOURCE_FROM_APP
*/
- public void onActionClicked(String key, @Nullable Notification.Action action, int source) {
+ public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+ @Source int source) {
}
/**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 6792c69..bec654a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,7 +16,6 @@
package android.service.notification;
-import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
@@ -126,6 +125,15 @@
private static final String STATE_TAG = "state";
private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
+ // zen policy visual effects attributes
+ private static final String SHOW_ATT_FULL_SCREEN_INTENT = "showFullScreenIntent";
+ private static final String SHOW_ATT_LIGHTS = "showLights";
+ private static final String SHOW_ATT_PEEK = "shoePeek";
+ private static final String SHOW_ATT_STATUS_BAR_ICONS = "showStatusBarIcons";
+ private static final String SHOW_ATT_BADGES = "showBadges";
+ private static final String SHOW_ATT_AMBIENT = "showAmbient";
+ private static final String SHOW_ATT_NOTIFICATION_LIST = "showNotificationList";
+
private static final String CONDITION_ATT_ID = "id";
private static final String CONDITION_ATT_SUMMARY = "summary";
private static final String CONDITION_ATT_LINE1 = "line1";
@@ -134,6 +142,8 @@
private static final String CONDITION_ATT_STATE = "state";
private static final String CONDITION_ATT_FLAGS = "flags";
+ private static final String ZEN_POLICY_TAG = "zen_policy";
+
private static final String MANUAL_TAG = "manual";
private static final String AUTOMATIC_TAG = "automatic";
@@ -650,6 +660,7 @@
rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
+ rt.zenPolicy = readZenPolicyXml(parser);
return rt;
}
@@ -676,6 +687,9 @@
if (rule.condition != null) {
writeConditionXml(rule.condition, out);
}
+ if (rule.zenPolicy != null) {
+ writeZenPolicyXml(rule.zenPolicy, out);
+ }
out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified));
}
@@ -706,6 +720,141 @@
out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
}
+ /**
+ * Read the zen policy from xml
+ * Returns null if no zen policy exists
+ */
+ public static ZenPolicy readZenPolicyXml(XmlPullParser parser) {
+ boolean policySet = false;
+
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+ final int calls = safeInt(parser, ALLOW_ATT_CALLS_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
+ final int messages = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
+ final int repeatCallers = safeInt(parser, ALLOW_ATT_REPEAT_CALLERS, ZenPolicy.STATE_UNSET);
+ final int alarms = safeInt(parser, ALLOW_ATT_ALARMS, ZenPolicy.STATE_UNSET);
+ final int media = safeInt(parser, ALLOW_ATT_MEDIA, ZenPolicy.STATE_UNSET);
+ final int system = safeInt(parser, ALLOW_ATT_SYSTEM, ZenPolicy.STATE_UNSET);
+ final int events = safeInt(parser, ALLOW_ATT_EVENTS, ZenPolicy.STATE_UNSET);
+ final int reminders = safeInt(parser, ALLOW_ATT_REMINDERS, ZenPolicy.STATE_UNSET);
+
+ if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) {
+ builder.allowCalls(calls);
+ policySet = true;
+ }
+ if (messages != ZenPolicy.PEOPLE_TYPE_UNSET) {
+ builder.allowMessages(messages);
+ policySet = true;
+ }
+ if (repeatCallers != ZenPolicy.STATE_UNSET) {
+ builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (alarms != ZenPolicy.STATE_UNSET) {
+ builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (media != ZenPolicy.STATE_UNSET) {
+ builder.allowMedia(media == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (system != ZenPolicy.STATE_UNSET) {
+ builder.allowSystem(system == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (events != ZenPolicy.STATE_UNSET) {
+ builder.allowEvents(events == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (reminders != ZenPolicy.STATE_UNSET) {
+ builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+
+ final int fullScreenIntent = safeInt(parser, SHOW_ATT_FULL_SCREEN_INTENT,
+ ZenPolicy.STATE_UNSET);
+ final int lights = safeInt(parser, SHOW_ATT_LIGHTS, ZenPolicy.STATE_UNSET);
+ final int peek = safeInt(parser, SHOW_ATT_PEEK, ZenPolicy.STATE_UNSET);
+ final int statusBar = safeInt(parser, SHOW_ATT_STATUS_BAR_ICONS, ZenPolicy.STATE_UNSET);
+ final int badges = safeInt(parser, SHOW_ATT_BADGES, ZenPolicy.STATE_UNSET);
+ final int ambient = safeInt(parser, SHOW_ATT_AMBIENT, ZenPolicy.STATE_UNSET);
+ final int notificationList = safeInt(parser, SHOW_ATT_NOTIFICATION_LIST,
+ ZenPolicy.STATE_UNSET);
+
+ if (fullScreenIntent != ZenPolicy.STATE_UNSET) {
+ builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (lights != ZenPolicy.STATE_UNSET) {
+ builder.showLights(lights == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (peek != ZenPolicy.STATE_UNSET) {
+ builder.showPeeking(peek == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (statusBar != ZenPolicy.STATE_UNSET) {
+ builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (badges != ZenPolicy.STATE_UNSET) {
+ builder.showBadges(badges == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (ambient != ZenPolicy.STATE_UNSET) {
+ builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+ if (notificationList != ZenPolicy.STATE_UNSET) {
+ builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW);
+ policySet = true;
+ }
+
+ if (policySet) {
+ return builder.build();
+ }
+ return null;
+ }
+
+ /**
+ * Writes zen policy to xml
+ */
+ public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out)
+ throws IOException {
+ writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out);
+ writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out);
+ writeZenPolicyState(ALLOW_ATT_REPEAT_CALLERS, policy.getPriorityCategoryRepeatCallers(),
+ out);
+ writeZenPolicyState(ALLOW_ATT_ALARMS, policy.getPriorityCategoryAlarms(), out);
+ writeZenPolicyState(ALLOW_ATT_MEDIA, policy.getPriorityCategoryMedia(), out);
+ writeZenPolicyState(ALLOW_ATT_SYSTEM, policy.getPriorityCategorySystem(), out);
+ writeZenPolicyState(ALLOW_ATT_REMINDERS, policy.getPriorityCategoryReminders(), out);
+ writeZenPolicyState(ALLOW_ATT_EVENTS, policy.getPriorityCategoryEvents(), out);
+
+ writeZenPolicyState(SHOW_ATT_FULL_SCREEN_INTENT, policy.getVisualEffectFullScreenIntent(),
+ out);
+ writeZenPolicyState(SHOW_ATT_LIGHTS, policy.getVisualEffectLights(), out);
+ writeZenPolicyState(SHOW_ATT_PEEK, policy.getVisualEffectPeek(), out);
+ writeZenPolicyState(SHOW_ATT_STATUS_BAR_ICONS, policy.getVisualEffectStatusBar(), out);
+ writeZenPolicyState(SHOW_ATT_BADGES, policy.getVisualEffectBadge(), out);
+ writeZenPolicyState(SHOW_ATT_AMBIENT, policy.getVisualEffectAmbient(), out);
+ writeZenPolicyState(SHOW_ATT_NOTIFICATION_LIST, policy.getVisualEffectNotificationList(),
+ out);
+ }
+
+ private static void writeZenPolicyState(String attr, int val, XmlSerializer out)
+ throws IOException {
+ if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM)
+ || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) {
+ if (val != ZenPolicy.PEOPLE_TYPE_UNSET) {
+ out.attribute(null, attr, Integer.toString(val));
+ }
+ } else {
+ if (val != ZenPolicy.STATE_UNSET) {
+ out.attribute(null, attr, Integer.toString(val));
+ }
+ }
+ }
+
public static boolean isValidHour(int val) {
return val >= 0 && val < 24;
}
@@ -798,7 +947,7 @@
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
- priorityCategories |= PRIORITY_CATEGORY_REMINDERS;
+ priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
}
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS,
@@ -839,15 +988,30 @@
priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
}
- if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
+ boolean suppressFullScreenIntent = !zenPolicy.isVisualEffectAllowed(
+ ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
- defaultPolicy))) {
+ defaultPolicy));
+
+ boolean suppressLights = !zenPolicy.isVisualEffectAllowed(
+ ZenPolicy.VISUAL_EFFECT_LIGHTS,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
+ defaultPolicy));
+
+ boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
+ ZenPolicy.VISUAL_EFFECT_AMBIENT,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
+ defaultPolicy));
+
+ if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+ }
+
+ if (suppressFullScreenIntent) {
suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
}
- if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_LIGHTS,
- isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
- defaultPolicy))) {
+ if (suppressLights) {
suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
}
@@ -855,6 +1019,7 @@
isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK,
defaultPolicy))) {
suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
}
if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
@@ -869,9 +1034,7 @@
suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
}
- if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_AMBIENT,
- isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
- defaultPolicy))) {
+ if (suppressAmbient) {
suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
}
@@ -898,14 +1061,31 @@
case ZenPolicy.PEOPLE_TYPE_ANYONE:
return Policy.PRIORITY_SENDERS_ANY;
case ZenPolicy.PEOPLE_TYPE_CONTACTS:
+
return Policy.PRIORITY_SENDERS_CONTACTS;
case ZenPolicy.PEOPLE_TYPE_STARRED:
default:
return Policy.PRIORITY_SENDERS_STARRED;
}
-
}
+
+ /**
+ * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType
+ */
+ public static @ZenPolicy.PeopleType int getZenPolicySenders(int senders) {
+ switch (senders) {
+ case Policy.PRIORITY_SENDERS_ANY:
+ return ZenPolicy.PEOPLE_TYPE_ANYONE;
+ case Policy.PRIORITY_SENDERS_CONTACTS:
+ return ZenPolicy.PEOPLE_TYPE_CONTACTS;
+ case Policy.PRIORITY_SENDERS_STARRED:
+ default:
+ return ZenPolicy.PEOPLE_TYPE_STARRED;
+ }
+ }
+
+
public Policy toNotificationPolicy() {
int priorityCategories = 0;
int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 194147c..6392704 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -327,6 +327,32 @@
}
/**
+ * Whether this policy hides all visual effects
+ * @hide
+ */
+ public boolean shouldHideAllVisualEffects() {
+ for (int i = 0; i < mVisualEffects.size(); i++) {
+ if (mVisualEffects.get(i) != STATE_DISALLOW) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Whether this policy shows all visual effects
+ * @hide
+ */
+ public boolean shouldShowAllVisualEffects() {
+ for (int i = 0; i < mVisualEffects.size(); i++) {
+ if (mVisualEffects.get(i) != STATE_ALLOW) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Builder class for {@link ZenPolicy} objects.
* Provides a convenient way to set the various fields of a {@link ZenPolicy}. If a field
* is not set, it is (@link STATE_UNSET} and will not change the current set policy.
@@ -339,6 +365,17 @@
}
/**
+ * @hide
+ */
+ public Builder(ZenPolicy policy) {
+ if (policy != null) {
+ mZenPolicy = policy.copy();
+ } else {
+ mZenPolicy = new ZenPolicy();
+ }
+ }
+
+ /**
* Builds the current ZenPolicy.
*/
public ZenPolicy build() {
@@ -533,6 +570,34 @@
}
/**
+ * Whether to allow {@link PriorityCategory} sounds to play when DND is active.
+ * @hide
+ */
+ public Builder allowCategory(@PriorityCategory int category, boolean allow) {
+ switch (category) {
+ case PRIORITY_CATEGORY_ALARMS:
+ allowAlarms(allow);
+ break;
+ case PRIORITY_CATEGORY_MEDIA:
+ allowMedia(allow);
+ break;
+ case PRIORITY_CATEGORY_SYSTEM:
+ allowSystem(allow);
+ break;
+ case PRIORITY_CATEGORY_REMINDERS:
+ allowReminders(allow);
+ break;
+ case PRIORITY_CATEGORY_EVENTS:
+ allowEvents(allow);
+ break;
+ case PRIORITY_CATEGORY_REPEAT_CALLERS:
+ allowRepeatCallers(allow);
+ break;
+ }
+ return this;
+ }
+
+ /**
* Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted
* by DND are shown.
*/
@@ -601,6 +666,38 @@
show ? STATE_ALLOW : STATE_DISALLOW);
return this;
}
+
+ /**
+ * Whether notifications intercepted by DND are prevented from appearing for
+ * {@link VisualEffect}
+ * @hide
+ */
+ public Builder showVisualEffect(@VisualEffect int effect, boolean show) {
+ switch (effect) {
+ case VISUAL_EFFECT_FULL_SCREEN_INTENT:
+ showFullScreenIntent(show);
+ break;
+ case VISUAL_EFFECT_LIGHTS:
+ showLights(show);
+ break;
+ case VISUAL_EFFECT_PEEK:
+ showPeeking(show);
+ break;
+ case VISUAL_EFFECT_STATUS_BAR:
+ showStatusBarIcons(show);
+ break;
+ case VISUAL_EFFECT_BADGE:
+ showBadges(show);
+ break;
+ case VISUAL_EFFECT_AMBIENT:
+ showInAmbientDisplay(show);
+ break;
+ case VISUAL_EFFECT_NOTIFICATION_LIST:
+ showInNotificationList(show);
+ break;
+ }
+ return this;
+ }
}
@Override
@@ -640,8 +737,8 @@
.append('{')
.append("priorityCategories=[").append(priorityCategoriesToString())
.append("], visualEffects=[").append(visualEffectsToString())
- .append(", priorityCalls=").append(stateToString(mPriorityCalls))
- .append("], priorityMessages=").append(stateToString(mPriorityMessages))
+ .append("], priorityCalls=").append(peopleTypeToString(mPriorityCalls))
+ .append(", priorityMessages=").append(peopleTypeToString(mPriorityMessages))
.append('}')
.toString();
}
@@ -726,7 +823,23 @@
case STATE_ALLOW:
return "allow";
}
- return null;
+ return "invalidState{" + state + "}";
+ }
+
+ private String peopleTypeToString(@PeopleType int peopleType) {
+ switch (peopleType) {
+ case PEOPLE_TYPE_ANYONE:
+ return "anyone";
+ case PEOPLE_TYPE_CONTACTS:
+ return "contacts";
+ case PEOPLE_TYPE_NONE:
+ return "none";
+ case PEOPLE_TYPE_STARRED:
+ return "starred_contacts";
+ case STATE_UNSET:
+ return "unset";
+ }
+ return "invalidPeopleType{" + peopleType + "}";
}
@Override
@@ -859,27 +972,6 @@
/**
* @hide
*/
- public boolean areValuesSet() {
- return getPriorityCategoryReminders() != STATE_UNSET
- || getPriorityCategoryEvents() != STATE_UNSET
- || getPriorityCategoryMessages() != STATE_UNSET
- || getPriorityCategoryCalls() != STATE_UNSET
- || getPriorityCategoryRepeatCallers() != STATE_UNSET
- || getPriorityCategoryAlarms() != STATE_UNSET
- || getPriorityCategoryMedia() != STATE_UNSET
- || getPriorityCategorySystem() != STATE_UNSET
- || getVisualEffectFullScreenIntent() != STATE_UNSET
- || getVisualEffectLights() != STATE_UNSET
- || getVisualEffectPeek() != STATE_UNSET
- || getVisualEffectStatusBar() != STATE_UNSET
- || getVisualEffectBadge() != STATE_UNSET
- || getVisualEffectAmbient() != STATE_UNSET
- || getVisualEffectNotificationList() != STATE_UNSET;
- }
-
- /**
- * @hide
- */
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 4b81a72..6b569cf 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -15,6 +15,7 @@
*/
package android.service.quicksettings;
+import android.annotation.Nullable;
import android.graphics.drawable.Icon;
import android.os.IBinder;
import android.os.Parcel;
@@ -62,6 +63,7 @@
private IBinder mToken;
private Icon mIcon;
private CharSequence mLabel;
+ private CharSequence mSubtitle;
private CharSequence mContentDescription;
// Default to active until clients of the new API can update.
private int mState = STATE_ACTIVE;
@@ -152,6 +154,22 @@
}
/**
+ * Gets the current subtitle for the tile.
+ */
+ @Nullable
+ public CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ /**
+ * Set the subtitle for the tile. Will be displayed as the secondary label.
+ * @param subtitle the subtitle to show.
+ */
+ public void setSubtitle(@Nullable CharSequence subtitle) {
+ this.mSubtitle = subtitle;
+ }
+
+ /**
* Gets the current content description for the tile.
*/
public CharSequence getContentDescription() {
@@ -195,6 +213,7 @@
}
dest.writeInt(mState);
TextUtils.writeToParcel(mLabel, dest, flags);
+ TextUtils.writeToParcel(mSubtitle, dest, flags);
TextUtils.writeToParcel(mContentDescription, dest, flags);
}
@@ -206,6 +225,7 @@
}
mState = source.readInt();
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java
index d100f12..680e85f 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -55,6 +55,13 @@
}
/**
+ * @return the set of items at index n
+ */
+ public ArraySet<T> get(int n) {
+ return mData.get(n);
+ }
+
+ /**
* Remove a value from index n.
* @return TRUE when the value existed at the given index and removed, FALSE otherwise.
*/
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9dfd43c..330d72f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -107,12 +107,6 @@
*/
void endProlongedAnimations();
- // Re-evaluate the current orientation from the caller's state.
- // If there is a change, the new Configuration is returned and the
- // caller must call setNewConfiguration() sometime later.
- Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
- IBinder freezeThisOneIfNeeded, int displayId);
-
void startFreezingScreen(int exitAnim, int enterAnim);
void stopFreezingScreen();
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
new file mode 100644
index 0000000..7b9f78e
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import static android.view.InsetsState.INSET_SIDE_BOTTOM;
+import static android.view.InsetsState.INSET_SIDE_LEFT;
+import static android.view.InsetsState.INSET_SIDE_RIGHT;
+import static android.view.InsetsState.INSET_SIDE_TOP;
+
+import android.annotation.Nullable;
+import android.graphics.Insets;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.UidProto.Sync;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.SparseSetArray;
+import android.view.InsetsState.InsetSide;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.WindowInsets.Type.InsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Implements {@link WindowInsetsAnimationController}
+ * @hide
+ */
+@VisibleForTesting
+public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
+
+ private final WindowInsetsAnimationControlListener mListener;
+ private final SparseArray<InsetsSourceConsumer> mConsumers;
+ private final SparseIntArray mTypeSideMap = new SparseIntArray();
+ private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>();
+
+ /** @see WindowInsetsAnimationController#getHiddenStateInsets */
+ private final Insets mHiddenInsets;
+
+ /** @see WindowInsetsAnimationController#getShownStateInsets */
+ private final Insets mShownInsets;
+ private final Matrix mTmpMatrix = new Matrix();
+ private final InsetsState mInitialInsetsState;
+ private final @InsetType int mTypes;
+ private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
+
+ private Insets mCurrentInsets;
+
+ @VisibleForTesting
+ public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
+ InsetsState state, WindowInsetsAnimationControlListener listener,
+ @InsetType int types,
+ Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
+ mConsumers = consumers;
+ mListener = listener;
+ mTypes = types;
+ mTransactionApplierSupplier = transactionApplierSupplier;
+ mInitialInsetsState = new InsetsState(state);
+ mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
+ mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
+ null /* typeSideMap */);
+ mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
+ mTypeSideMap);
+ buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);
+
+ // TODO: Check for controllability first and wait for IME if needed.
+ listener.onReady(this, types);
+ }
+
+ @Override
+ public Insets getHiddenStateInsets() {
+ return mHiddenInsets;
+ }
+
+ @Override
+ public Insets getShownStateInsets() {
+ return mShownInsets;
+ }
+
+ @Override
+ public Insets getCurrentInsets() {
+ return mCurrentInsets;
+ }
+
+ @Override
+ @InsetType
+ public int getTypes() {
+ return mTypes;
+ }
+
+ @Override
+ public void changeInsets(Insets insets) {
+ insets = sanitize(insets);
+ final Insets offset = Insets.subtract(mShownInsets, insets);
+ ArrayList<SurfaceParams> params = new ArrayList<>();
+ if (offset.left != 0) {
+ updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
+ }
+ if (offset.top != 0) {
+ updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
+ }
+ if (offset.right != 0) {
+ updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
+ }
+ if (offset.bottom != 0) {
+ updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
+ }
+ SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
+ applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+ mCurrentInsets = insets;
+ }
+
+ @Override
+ public void finish(int shownTypes) {
+ // TODO
+ }
+
+ private Insets calculateInsets(InsetsState state, Rect frame,
+ SparseArray<InsetsSourceConsumer> consumers, boolean shown,
+ @Nullable @InsetSide SparseIntArray typeSideMap) {
+ for (int i = consumers.size() - 1; i >= 0; i--) {
+ state.getSource(consumers.valueAt(i).getType()).setVisible(shown);
+ }
+ return getInsetsFromState(state, frame, typeSideMap);
+ }
+
+ private Insets getInsetsFromState(InsetsState state, Rect frame,
+ @Nullable @InsetSide SparseIntArray typeSideMap) {
+ return state.calculateInsets(frame, false /* isScreenRound */,
+ false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
+ .getSystemWindowInsets();
+ }
+
+ private Insets sanitize(Insets insets) {
+ return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
+ }
+
+ private void updateLeashesForSide(@InsetSide int side, int inset,
+ ArrayList<SurfaceParams> surfaceParams) {
+ ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
+ // TODO: Implement behavior when inset spans over multiple types
+ for (int i = items.size() - 1; i >= 0; i--) {
+ final InsetsSourceConsumer consumer = items.valueAt(i);
+ final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
+ final SurfaceControl leash = consumer.getControl().getLeash();
+ mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
+ addTranslationToMatrix(side, inset, mTmpMatrix);
+ surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
+ }
+ }
+
+ private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
+ switch (side) {
+ case INSET_SIDE_LEFT:
+ m.postTranslate(-inset, 0);
+ break;
+ case INSET_SIDE_TOP:
+ m.postTranslate(0, -inset);
+ break;
+ case INSET_SIDE_RIGHT:
+ m.postTranslate(inset, 0);
+ break;
+ case INSET_SIDE_BOTTOM:
+ m.postTranslate(0, inset);
+ break;
+ }
+ }
+
+ private static void buildTypeSourcesMap(SparseIntArray typeSideMap,
+ SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
+ SparseArray<InsetsSourceConsumer> consumers) {
+ for (int i = typeSideMap.size() - 1; i >= 0; i--) {
+ int type = typeSideMap.keyAt(i);
+ int side = typeSideMap.valueAt(i);
+ sideSourcesMap.add(side, consumers.get(type));
+ }
+ }
+}
+
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index fb4f9c0..4ab1f26 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* Implements {@link WindowInsetsController} on the client.
@@ -41,6 +42,7 @@
private final ViewRootImpl mViewRoot;
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+ private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
public InsetsController(ViewRootImpl viewRoot) {
mViewRoot = viewRoot;
@@ -67,9 +69,11 @@
/**
* @see InsetsState#calculateInsets
*/
- WindowInsets calculateInsets(boolean isScreenRound,
+ @VisibleForTesting
+ public WindowInsets calculateInsets(boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout) {
- return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
+ return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
+ null /* typeSideMap */);
}
/**
@@ -116,6 +120,28 @@
}
}
+ @Override
+ public void controlWindowInsetsAnimation(@InsetType int types,
+ WindowInsetsAnimationControlListener listener) {
+
+ // TODO: Check whether we already have a controller.
+ final ArraySet<Integer> internalTypes = mState.toInternalType(types);
+ final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
+ if (consumer.getControl() != null) {
+ consumers.put(consumer.getType(), consumer);
+ } else {
+ // TODO: Let calling app know it's not possible, or wait
+ // TODO: Remove it from types
+ }
+ }
+ final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
+ mFrame, mState, listener, types,
+ () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
+ mAnimationControls.add(controller);
+ }
+
private void applyLocalVisibilityOverride() {
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
@@ -134,7 +160,8 @@
return controller;
}
- void notifyVisibilityChanged() {
+ @VisibleForTesting
+ public void notifyVisibilityChanged() {
mViewRoot.notifyInsetsChanged();
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 0cb8ad7..f8148a9 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -70,7 +70,8 @@
*
* @param relativeFrame The frame to calculate the insets relative to.
* @param ignoreVisibility If true, always reports back insets even if source isn't visible.
- * @return The resulting insets.
+ * @return The resulting insets. The contract is that only one side will be occupied by a
+ * source.
*/
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
if (!ignoreVisibility && !mVisible) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 689b14f..63025dc 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -17,12 +17,15 @@
package android.view;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetType;
@@ -77,11 +80,30 @@
/** A shelf is the same as the navigation bar. */
public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "INSET_SIDE", value = {
+ INSET_SIDE_LEFT,
+ INSET_SIDE_TOP,
+ INSET_SIDE_RIGHT,
+ INSET_SIDE_BOTTOM,
+ INSET_SIDE_UNKNWON
+ })
+ public @interface InsetSide {}
+ static final int INSET_SIDE_LEFT = 0;
+ static final int INSET_SIDE_TOP = 1;
+ static final int INSET_SIDE_RIGHT = 2;
+ static final int INSET_SIDE_BOTTOM = 3;
+ static final int INSET_SIDE_UNKNWON = 4;
+
private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
public InsetsState() {
}
+ public InsetsState(InsetsState copy) {
+ set(copy);
+ }
+
/**
* Calculates {@link WindowInsets} based on the current source configuration.
*
@@ -89,7 +111,8 @@
* @return The calculated insets.
*/
public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
- boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+ boolean alwaysConsumeNavBar, DisplayCutout cutout,
+ @Nullable @InsetSide SparseIntArray typeSideMap) {
Insets systemInsets = Insets.NONE;
Insets maxInsets = Insets.NONE;
final Rect relativeFrame = new Rect(frame);
@@ -100,13 +123,13 @@
continue;
}
systemInsets = processSource(source, systemInsets, relativeFrame,
- false /* ignoreVisibility */);
+ false /* ignoreVisibility */, typeSideMap);
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
// target.
if (source.getType() != TYPE_IME) {
maxInsets = processSource(source, maxInsets, relativeFrameMax,
- true /* ignoreVisibility */);
+ true /* ignoreVisibility */, null /* typeSideMap */);
}
}
return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
@@ -114,13 +137,39 @@
}
private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
- boolean ignoreVisibility) {
+ boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
insets = Insets.add(currentInsets, insets);
relativeFrame.inset(insets);
+ if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
+ @InsetSide int insetSide = getInsetSide(currentInsets);
+ if (insetSide != INSET_SIDE_UNKNWON) {
+ typeSideMap.put(source.getType(), getInsetSide(currentInsets));
+ }
+ }
return insets;
}
+ /**
+ * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
+ * is set in order that this method returns a meaningful result.
+ */
+ private @InsetSide int getInsetSide(Insets insets) {
+ if (insets.left != 0) {
+ return INSET_SIDE_LEFT;
+ }
+ if (insets.top != 0) {
+ return INSET_SIDE_TOP;
+ }
+ if (insets.right != 0) {
+ return INSET_SIDE_RIGHT;
+ }
+ if (insets.bottom != 0) {
+ return INSET_SIDE_BOTTOM;
+ }
+ return INSET_SIDE_UNKNWON;
+ }
+
public InsetsSource getSource(@InternalInsetType int type) {
return mSources.computeIfAbsent(type, InsetsSource::new);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
similarity index 72%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
rename to core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 807edf6..0270acb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -11,24 +11,22 @@
* 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.shared.system;
+package android.view;
-import android.graphics.HardwareRenderer;
import android.graphics.Matrix;
import android.graphics.Rect;
-import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import android.view.View;
-import android.view.ViewRootImpl;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.util.function.Consumer;
/**
* Helper class to apply surface transactions in sync with RenderThread.
+ * @hide
*/
public class SyncRtSurfaceTransactionApplier {
@@ -54,30 +52,32 @@
if (mTargetViewRootImpl == null) {
return;
}
- mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
- @Override
- public void onFrameDraw(long frame) {
- if (mTargetSurface == null || !mTargetSurface.isValid()) {
- return;
- }
- Transaction t = new Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SurfaceParams surfaceParams = params[i];
- SurfaceControl surface = surfaceParams.surface;
- t.deferTransactionUntilSurface(surface, mTargetSurface, frame);
- applyParams(t, surfaceParams, mTmpFloat9);
- }
- t.setEarlyWakeup();
- t.apply();
+ mTargetViewRootImpl.registerRtFrameCallback(frame -> {
+ if (mTargetSurface == null || !mTargetSurface.isValid()) {
+ return;
}
+ Transaction t = new Transaction();
+ for (int i = params.length - 1; i >= 0; i--) {
+ SurfaceParams surfaceParams = params[i];
+ SurfaceControl surface = surfaceParams.surface;
+ t.deferTransactionUntilSurface(surface, mTargetSurface, frame);
+ applyParams(t, surfaceParams, mTmpFloat9);
+ }
+ t.setEarlyWakeup();
+ t.apply();
});
// Make sure a frame gets scheduled.
mTargetViewRootImpl.getView().invalidate();
}
- public static void applyParams(TransactionCompat t, SurfaceParams params) {
- applyParams(t.mTransaction, params, t.mTmpValues);
+ public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
+ t.setMatrix(params.surface, params.matrix, tmpFloat9);
+ t.setWindowCrop(params.surface, params.windowCrop);
+ t.setAlpha(params.surface, params.alpha);
+ t.setLayer(params.surface, params.layer);
+ t.setCornerRadius(params.surface, params.cornerRadius);
+ t.show(params.surface);
}
/**
@@ -109,15 +109,6 @@
}
}
- private static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
- t.setMatrix(params.surface, params.matrix, tmpFloat9);
- t.setWindowCrop(params.surface, params.windowCrop);
- t.setAlpha(params.surface, params.alpha);
- t.setLayer(params.surface, params.layer);
- t.setCornerRadius(params.surface, params.cornerRadius);
- t.show(params.surface);
- }
-
public static class SurfaceParams {
/**
@@ -129,9 +120,9 @@
* @param matrix Matrix to apply.
* @param windowCrop Crop to apply.
*/
- public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
+ public SurfaceParams(SurfaceControl surface, float alpha, Matrix matrix,
Rect windowCrop, int layer, float cornerRadius) {
- this.surface = surface.mSurfaceControl;
+ this.surface = surface;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
this.windowCrop = new Rect(windowCrop);
@@ -139,11 +130,22 @@
this.cornerRadius = cornerRadius;
}
- final SurfaceControl surface;
- final float alpha;
- final Matrix matrix;
- final Rect windowCrop;
- final int layer;
+ @VisibleForTesting
+ public final SurfaceControl surface;
+
+ @VisibleForTesting
+ public final float alpha;
+
+ @VisibleForTesting
final float cornerRadius;
+
+ @VisibleForTesting
+ public final Matrix matrix;
+
+ @VisibleForTesting
+ public final Rect windowCrop;
+
+ @VisibleForTesting
+ public final int layer;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 468d922..57635ef 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9046,17 +9046,16 @@
* {@code onCreate()} and associate it with the root view of the activity:
*
* <pre>
- * ContentCaptureManager mgr = getSystemService(ContentCaptureManager.class);
- * if (mgr != null && mgr.isContentCaptureEnabled()) {
- * View rootView = findViewById(R.my_root_view);
- * ContentCaptureSession session = mgr.createContentCaptureSession(new
+ * ContentCaptureSession oldSession = rootView.getContentCaptureSession();
+ * if (oldSession != null) {
+ * ContentCaptureSession newSession = oldSession.createContentCaptureSession(new
* ContentCaptureContext.Builder().setUri(myUrl).build());
- * rootView.setContentCaptureSession(session);
+ * rootView.setContentCaptureSession(newSession);
* }
* </pre>
*
* @param contentCaptureSession a session created by
- * {@link ContentCaptureManager#createContentCaptureSession(
+ * {@link ContentCaptureSession#createContentCaptureSession(
* android.view.contentcapture.ContentCaptureContext)}.
*/
public void setContentCaptureSession(@NonNull ContentCaptureSession contentCaptureSession) {
@@ -10489,6 +10488,7 @@
*
* @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
* a window.
+ * @see Window#getInsetsController()
* @hide pending unhide
*/
public @Nullable WindowInsetsController getWindowInsetsController() {
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
new file mode 100644
index 0000000..b27a23d
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.inputmethod.EditorInfo;
+
+/**
+ * Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
+ * @hide pending unhide
+ */
+public interface WindowInsetsAnimationControlListener {
+
+ /**
+ * Gets called as soon as the animation is ready to be controlled. This may be
+ * delayed when the IME needs to redraw because of an {@link EditorInfo} change, or when the
+ * window is starting up.
+ *
+ * @param controller The controller to control the inset animation.
+ * @param types The {@link InsetType}s it was able to gain control over. Note that this may be
+ * different than the types passed into
+ * {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
+ * wasn't able to gain the controls because it wasn't the IME target or not
+ * currently the window that's controlling the system bars.
+ */
+ void onReady(@NonNull WindowInsetsAnimationController controller, @InsetType int types);
+
+ /**
+ * Called when the window no longer has control over the requested types. If it loses control
+ * over one type, the whole control will be cancelled. If none of the requested types were
+ * available when requesting the control, the animation control will be cancelled immediately
+ * without {@link #onReady} being called.
+ */
+ void onCancelled();
+}
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
new file mode 100644
index 0000000..9de517d
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.graphics.Insets;
+import android.view.WindowInsets.Type.InsetType;
+
+/**
+ * Interface to control a window inset animation frame-by-frame.
+ * @hide pending unhide
+ */
+public interface WindowInsetsAnimationController {
+
+ /**
+ * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
+ *
+ * @return Insets when the windows this animation is controlling are fully hidden.
+ */
+ @NonNull Insets getHiddenStateInsets();
+
+ /**
+ * Retrieves the {@link Insets} when the windows this animation is controlling are fully shown.
+ * <p>
+ * In case the size of a window causing insets is changing in the middle of the animation, we
+ * execute that height change after this animation has finished.
+ *
+ * @return Insets when the windows this animation is controlling are fully shown.
+ */
+ @NonNull Insets getShownStateInsets();
+
+ /**
+ * @return The current insets on the window. These will follow any animation changes.
+ */
+ @NonNull Insets getCurrentInsets();
+
+ /**
+ * @return The {@link InsetType}s this object is currently controlling.
+ */
+ @InsetType int getTypes();
+
+ /**
+ * Modifies the insets by indirectly moving the windows around in the system that are causing
+ * window insets.
+ * <p>
+ * Note that this will <b>not</b> inform the view system of a full inset change via
+ * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
+ * animation. If you'd like to animate views during a window inset animation, use
+ * TODO add link to animation listeners.
+ * <p>
+ * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
+ * finished, i.e. once {@link #finish} has been called.
+ *
+ * @param insets The new insets to apply. Based on the requested insets, the system will
+ * calculate the positions of the windows in the system causing insets such that
+ * the resulting insets of that configuration will match the passed in parameter.
+ * Note that these insets are being clamped to the range from
+ * {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
+ */
+ void changeInsets(@NonNull Insets insets);
+
+ /**
+ * @param shownTypes The list of windows causing insets that should remain shown after finishing
+ * the animation.
+ */
+ void finish(@InsetType int shownTypes);
+}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 7be5f2e..a35be27 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetType;
/**
@@ -51,4 +52,15 @@
* would like to make disappear.
*/
void hide(@InsetType int types);
+
+ /**
+ * Lets the application control window inset animations in a frame-by-frame manner by modifying
+ * the position of the windows in the system causing insets directly.
+ *
+ * @param types The {@link InsetType}s the application has requested to control.
+ * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
+ * windows are ready to be controlled, among other callbacks.
+ */
+ void controlWindowInsetsAnimation(@InsetType int types,
+ @NonNull WindowInsetsAnimationControlListener listener);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 699b34a..56f973e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -77,7 +77,6 @@
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -2966,12 +2965,13 @@
if (afm == null) return null;
final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
- // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
- // fix (for example, take system status bar height into account) logic below
+ final Rect windowVisibleDisplayFrame = new Rect();
+ view.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
final int[] location = new int[2];
view.getLocationOnScreen(location);
- final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
- location[1] + view.getHeight());
+ final Rect rect = new Rect(location[0], location[1] - windowVisibleDisplayFrame.top,
+ location[0] + view.getWidth(),
+ location[1] - windowVisibleDisplayFrame.top + view.getHeight());
if (sVerbose) {
Log.v(TAG, "Coordinates for " + id + ": " + rect);
}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
new file mode 100644
index 0000000..5166831
--- /dev/null
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+
+/**
+ * A session that is explicitly created by the app (and hence is a descendant of
+ * {@link MainContentCaptureSession}).
+ *
+ * @hide
+ */
+final class ChildContentCaptureSession extends ContentCaptureSession {
+
+ @NonNull
+ private final MainContentCaptureSession mParent;
+
+ /**
+ * {@link ContentCaptureContext} set by client, or {@code null} when it's the
+ * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
+ * context.
+ *
+ * @hide
+ */
+ @NonNull
+ private final ContentCaptureContext mClientContext;
+
+ /** @hide */
+ protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent,
+ @NonNull ContentCaptureContext clientContext) {
+ mParent = parent;
+ mClientContext = Preconditions.checkNotNull(clientContext);
+ }
+
+ @Override
+ ContentCaptureSession newChild(@NonNull ContentCaptureContext context) {
+ // TODO(b/121033016): implement it
+ throw new UnsupportedOperationException("grand-children not implemented yet");
+ }
+
+ @Override
+ void flush() {
+ mParent.flush();
+ }
+
+ @Override
+ void onDestroy() {
+ mParent.notifyChildSessionFinished(mParent.mId, mId);
+ }
+
+ @Override
+ void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
+ mParent.notifyViewAppeared(mId, node);
+ }
+
+ @Override
+ void internalNotifyViewDisappeared(@NonNull AutofillId id) {
+ mParent.notifyViewDisappeared(mId, id);
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags) {
+ mParent.notifyViewTextChanged(mId, id, text, flags);
+ }
+ @Override
+ boolean isContentCaptureEnabled() {
+ return mParent.isContentCaptureEnabled();
+ }
+
+ @Override
+ void dump(String prefix, PrintWriter pw) {
+ if (mClientContext != null) {
+ // NOTE: we don't dump clientContent because it could have PII
+ pw.print(prefix); pw.println("hasClientContext");
+ }
+ super.dump(prefix, pw);
+ }
+}
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 9c11743..2d2987a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -21,6 +21,7 @@
import android.annotation.SystemApi;
import android.app.TaskInfo;
import android.content.ComponentName;
+import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -76,7 +77,7 @@
/**
* Flag indicating if this object has the app-provided context (which is set on
- * {@link ContentCaptureManager#createContentCaptureSession(ContentCaptureContext)}).
+ * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}).
*/
private final boolean mHasClientContext;
@@ -91,6 +92,9 @@
private final int mDisplayId;
private final int mFlags;
+ // Fields below are set by the service upon "delivery" and are not marshalled in the parcel
+ private @Nullable String mParentSessionId;
+
/** @hide */
public ContentCaptureContext(@Nullable ContentCaptureContext clientContext,
@NonNull ComponentName componentName, int taskId, int displayId, int flags) {
@@ -153,16 +157,33 @@
}
/**
- * Gets the activity associated with this context.
+ * Gets the activity associated with this context, or {@code null} when it is a child session.
*
* @hide
*/
@SystemApi
- public @NonNull ComponentName getActivityComponent() {
+ public @Nullable ComponentName getActivityComponent() {
return mComponentName;
}
/**
+ * Gets the id of the session that originated this session (through
+ * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}),
+ * or {@code null} if this is the main session associated with the Activity's {@link Context}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @Nullable ContentCaptureSessionId getParentSessionId() {
+ return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId);
+ }
+
+ /** @hide */
+ public void setParentSessionId(@NonNull String parentSessionId) {
+ mParentSessionId = parentSessionId;
+ }
+
+ /**
* Gets the ID of the display associated with this context, as defined by
* {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
*
@@ -242,6 +263,9 @@
pw.print("comp="); pw.print(ComponentName.flattenToShortString(mComponentName));
pw.print(", taskId="); pw.print(mTaskId);
pw.print(", displayId="); pw.print(mDisplayId);
+ if (mParentSessionId != null) {
+ pw.print(", parentId="); pw.print(mParentSessionId);
+ }
if (mFlags > 0) {
pw.print(", flags="); pw.print(mFlags);
}
@@ -262,6 +286,9 @@
.append(", taskId=").append(mTaskId)
.append(", displayId=").append(mDisplayId)
.append(", flags=").append(mFlags);
+ if (mParentSessionId != null) {
+ builder.append(", parentId=").append(mParentSessionId);
+ }
if (mExtras != null) {
// NOTE: cannot print because it could contain PII
builder.append(", hasExtras");
@@ -320,9 +347,9 @@
final int taskId = parcel.readInt();
final int displayId = parcel.readInt();
final int flags = parcel.readInt();
- return new ContentCaptureContext(clientContext, componentName, taskId,
- displayId, flags);
- }
+ return new ContentCaptureContext(clientContext, componentName, taskId, displayId,
+ flags);
+ }
}
@Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 5d8fe5f..9e3da92 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -29,15 +29,14 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-// TODO(b/111276913): add javadocs / implement Parcelable / implement
/** @hide */
@SystemApi
public final class ContentCaptureEvent implements Parcelable {
/** @hide */
- public static final int TYPE_ACTIVITY_DESTROYED = -2;
+ public static final int TYPE_SESSION_FINISHED = -2;
/** @hide */
- public static final int TYPE_ACTIVITY_CREATED = -1;
+ public static final int TYPE_SESSION_STARTED = -1;
/**
* Called when a node has been added to the screen and is visible to the user.
@@ -72,29 +71,32 @@
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
+ private final @NonNull String mSessionId;
private final int mType;
private final long mEventTime;
private final int mFlags;
private @Nullable AutofillId mId;
private @Nullable ViewNode mNode;
private @Nullable CharSequence mText;
+ private @Nullable String mParentSessionId;
+ private @Nullable ContentCaptureContext mClientContext;
/** @hide */
- public ContentCaptureEvent(int type, long eventTime, int flags) {
+ public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) {
+ mSessionId = sessionId;
mType = type;
mEventTime = eventTime;
mFlags = flags;
}
-
/** @hide */
- public ContentCaptureEvent(int type, int flags) {
- this(type, System.currentTimeMillis(), flags);
+ public ContentCaptureEvent(@NonNull String sessionId, int type, int flags) {
+ this(sessionId, type, System.currentTimeMillis(), flags);
}
/** @hide */
- public ContentCaptureEvent(int type) {
- this(type, /* flags= */ 0);
+ public ContentCaptureEvent(@NonNull String sessionId, int type) {
+ this(sessionId, type, /* flags= */ 0);
}
/** @hide */
@@ -103,13 +105,61 @@
return this;
}
+ /**
+ * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}.
+ *
+ * @hide
+ */
+ public ContentCaptureEvent setParentSessionId(@NonNull String parentSessionId) {
+ mParentSessionId = parentSessionId;
+ return this;
+ }
+
+ /**
+ * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}.
+ *
+ * @hide
+ */
+ public ContentCaptureEvent setClientContext(@NonNull ContentCaptureContext clientContext) {
+ mClientContext = clientContext;
+ return this;
+ }
+
/** @hide */
+ @NonNull
+ public String getSessionId() {
+ return mSessionId;
+ }
+
+ /**
+ * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}.
+ *
+ * @hide
+ */
+ @Nullable
+ public String getParentSessionId() {
+ return mParentSessionId;
+ }
+
+ /**
+ * Used by {@link #TYPE_SESSION_STARTED}.
+ *
+ * @hide
+ */
+ @Nullable
+ public ContentCaptureContext getClientContext() {
+ return mClientContext;
+ }
+
+ /** @hide */
+ @NonNull
public ContentCaptureEvent setViewNode(@NonNull ViewNode node) {
mNode = Preconditions.checkNotNull(node);
return this;
}
/** @hide */
+ @NonNull
public ContentCaptureEvent setText(@Nullable CharSequence text) {
mText = text;
return this;
@@ -183,7 +233,17 @@
pw.print(", id="); pw.print(mId);
}
if (mNode != null) {
- pw.print(", id="); pw.print(mNode.getAutofillId());
+ pw.print(", mNode.id="); pw.print(mNode.getAutofillId());
+ }
+ if (mSessionId != null) {
+ pw.print(", sessionId="); pw.print(mSessionId);
+ }
+ if (mParentSessionId != null) {
+ pw.print(", parentSessionId="); pw.print(mParentSessionId);
+ }
+ if (mText != null) {
+ // Cannot print content because could have PII
+ pw.print(", text="); pw.print(mText.length()); pw.print("_chars");
}
}
@@ -214,12 +274,19 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mSessionId);
parcel.writeInt(mType);
parcel.writeLong(mEventTime);
parcel.writeInt(mFlags);
parcel.writeParcelable(mId, flags);
ViewNode.writeToParcel(parcel, mNode, flags);
parcel.writeCharSequence(mText);
+ if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) {
+ parcel.writeString(mParentSessionId);
+ }
+ if (mType == TYPE_SESSION_STARTED) {
+ parcel.writeParcelable(mClientContext, flags);
+ }
}
public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -227,10 +294,12 @@
@Override
public ContentCaptureEvent createFromParcel(Parcel parcel) {
+ final String sessionId = parcel.readString();
final int type = parcel.readInt();
final long eventTime = parcel.readLong();
final int flags = parcel.readInt();
- final ContentCaptureEvent event = new ContentCaptureEvent(type, eventTime, flags);
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(sessionId, type, eventTime, flags);
final AutofillId id = parcel.readParcelable(null);
if (id != null) {
event.setAutofillId(id);
@@ -240,6 +309,12 @@
event.setViewNode(node);
}
event.setText(parcel.readCharSequence());
+ if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
+ event.setParentSessionId(parcel.readString());
+ }
+ if (type == TYPE_SESSION_STARTED) {
+ event.setClientContext(parcel.readParcelable(null));
+ }
return event;
}
@@ -252,6 +327,10 @@
/** @hide */
public static String getTypeAsString(@EventType int type) {
switch (type) {
+ case TYPE_SESSION_STARTED:
+ return "SESSION_STARTED";
+ case TYPE_SESSION_FINISHED:
+ return "SESSION_FINISHED";
case TYPE_VIEW_APPEARED:
return "VIEW_APPEARED";
case TYPE_VIEW_DISAPPEARED:
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 7fbbfb7..9830790 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -18,13 +18,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.annotation.UiThread;
import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.util.Log;
-import android.view.View;
import com.android.internal.util.Preconditions;
@@ -66,7 +66,7 @@
@NonNull
private final Handler mHandler;
- private ContentCaptureSession mMainSession;
+ private MainContentCaptureSession mMainSession;
/** @hide */
public ContentCaptureManager(@NonNull Context context,
@@ -93,46 +93,20 @@
}
/**
- * Creates a new {@link ContentCaptureSession}.
- *
- * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info.
- */
- @NonNull
- public ContentCaptureSession createContentCaptureSession(
- @NonNull ContentCaptureContext context) {
- if (DEBUG) Log.d(TAG, "createContentCaptureSession(): " + context);
- // TODO(b/121033016): for now we're updating the main session, but we need instead:
- // 1.Keep a list of sessions
- // 2.Making sure the applicationToken and componentName passed by
- // the activity is used on all of these sessions
- // 3.We might also need to delay the start of all of these sessions until
- // onActivityStarted() is called (and the main session is created).
- // 4.Close (and delete) these sessions when onActivityStopped() is called.
- // 5.Figure out whether each session will have its own mDisabled AtomicBoolean.
- if (mMainSession == null) {
- mMainSession = new ContentCaptureSession(mContext, mHandler, mService,
- mDisabled, Preconditions.checkNotNull(context));
- } else {
- throw new IllegalStateException("Manager already has a session: " + mMainSession);
- }
- return mMainSession;
- }
-
- /**
* Gets the main session associated with the context.
*
* <p>By default there's just one (associated with the activity lifecycle), but apps could
- * explicitly add more using {@link #createContentCaptureSession(ContentCaptureContext)}.
+ * explicitly add more using
+ * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}.
*
* @hide
*/
@NonNull
- public ContentCaptureSession getMainContentCaptureSession() {
- // TODO(b/121033016): figure out how to manage the "default" session when it support
- // multiple sessions (can't just be the first one, as it could be closed).
+ @UiThread
+ public MainContentCaptureSession getMainContentCaptureSession() {
if (mMainSession == null) {
- mMainSession = new ContentCaptureSession(mContext, mHandler, mService, mDisabled,
- /* contentCaptureContext= */ null);
+ mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
+ mDisabled);
if (VERBOSE) {
Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index f411cf7..9f666a4 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -15,62 +15,35 @@
*/
package android.view.contentcapture;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
import static android.view.contentcapture.ContentCaptureManager.DEBUG;
import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ParceledListSlice;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
-import android.util.TimeUtils;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
-import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Session used to notify a system-provided Content Capture service about events associated with
* views.
*/
-public final class ContentCaptureSession implements AutoCloseable {
-
- /*
- * IMPLEMENTATION NOTICE:
- *
- * All methods in this class should return right away, or do the real work in a handler thread.
- *
- * Hence, the only field that must be thread-safe is mEnabled, which is called at the
- * beginning of every method.
- */
-
- private static final String TAG = ContentCaptureSession.class.getSimpleName();
+public abstract class ContentCaptureSession implements AutoCloseable {
/**
* Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the
+ *
* thext change was caused by user input (for example, through IME).
*/
public static final int FLAG_USER_INPUT = 0x1;
@@ -110,106 +83,41 @@
*/
public static final int STATE_DISABLED_DUPLICATED_ID = 4;
- /**
- * Handler message used to flush the buffer.
- */
- private static final int MSG_FLUSH = 1;
+ private static final int INITIAL_CHILDREN_CAPACITY = 5;
- /**
- * Maximum number of events that are buffered before sent to the app.
- */
- // TODO(b/121044064): use settings
- private static final int MAX_BUFFER_SIZE = 100;
-
- /**
- * Frequency the buffer is flushed if stale.
- */
- // TODO(b/121044064): use settings
- private static final int FLUSHING_FREQUENCY_MS = 5_000;
-
-
- /**
- * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
- * @hide
- */
- public static final String EXTRA_BINDER = "binder";
+ /** @hide */
+ protected final String mTag = getClass().getSimpleName();
private final CloseGuard mCloseGuard = CloseGuard.get();
- @NonNull
- private final AtomicBoolean mDisabled;
-
- @NonNull
- private final Context mContext;
-
- @NonNull
- private final Handler mHandler;
-
- /**
- * Interface to the system_server binder object - it's only used to start the session (and
- * notify when the session is finished).
- */
+ /** @hide */
@Nullable
- private final IContentCaptureManager mSystemServerInterface;
-
- /**
- * Direct interface to the service binder object - it's used to send the events, including the
- * last ones (when the session is finished)
- */
- @Nullable
- private IContentCaptureDirectManager mDirectServiceInterface;
- @Nullable
- private DeathRecipient mDirectServiceVulture;
-
- @Nullable
- private final String mId = UUID.randomUUID().toString();
+ protected final String mId = UUID.randomUUID().toString();
private int mState = STATE_UNKNOWN;
- @Nullable
- private IBinder mApplicationToken;
-
- @Nullable
- private ComponentName mComponentName;
-
- /**
- * List of events held to be sent as a batch.
- */
- // TODO(b/111276913): once we support multiple sessions, we need to move the buffer of events
- // to its own class so it's shared by all sessions
- @Nullable
- private ArrayList<ContentCaptureEvent> mEvents;
-
- // Used just for debugging purposes (on dump)
- private long mNextFlush;
-
// Lazily created on demand.
private ContentCaptureSessionId mContentCaptureSessionId;
/**
- * {@link ContentCaptureContext} set by client, or {@code null} when it's the
- * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
- * context.
+ * List of children session.
*/
+ // TODO(b/121033016): need to synchonize access, either by changing on handler or UI thread
+ // (for now there's no handler on this class, so we need to wait for the next refactoring),
+ // most likely the former (as we have no guarantee that createContentCaptureSession()
+ // it will be called in the UiThread; for example, WebView most likely won't call on it)
@Nullable
- private final ContentCaptureContext mClientContext;
+ private ArrayList<ContentCaptureSession> mChildren;
/** @hide */
- protected ContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
- @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled,
- @Nullable ContentCaptureContext clientContext) {
- mContext = context;
- mHandler = handler;
- mSystemServerInterface = systemServerInterface;
- mDisabled = disabled;
- mClientContext = clientContext;
+ protected ContentCaptureSession() {
mCloseGuard.open("destroy");
}
/**
* Gets the id used to identify this session.
*/
- public ContentCaptureSessionId getContentCaptureSessionId() {
+ public final ContentCaptureSessionId getContentCaptureSessionId() {
if (mContentCaptureSessionId == null) {
mContentCaptureSessionId = new ContentCaptureSessionId(mId);
}
@@ -217,53 +125,74 @@
}
/**
- * Starts this session.
+ * Creates a new {@link ContentCaptureSession}.
*
- * @hide
+ * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info.
*/
- void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
- if (!isContentCaptureEnabled()) return;
-
- if (VERBOSE) {
- Log.v(TAG, "start(): token=" + applicationToken + ", comp="
- + ComponentName.flattenToShortString(activityComponent));
+ @NonNull
+ public final ContentCaptureSession createContentCaptureSession(
+ @NonNull ContentCaptureContext context) {
+ final ContentCaptureSession child = newChild(context);
+ if (DEBUG) {
+ Log.d(mTag, "createContentCaptureSession(" + context + ": parent=" + mId + ", child= "
+ + child.mId);
}
-
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleStartSession, this,
- applicationToken, activityComponent));
+ if (mChildren == null) {
+ mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY);
+ }
+ mChildren.add(child);
+ return child;
}
+ abstract ContentCaptureSession newChild(@NonNull ContentCaptureContext context);
+
/**
* Flushes the buffered events to the service.
- *
- * @hide
*/
- void flush() {
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleForceFlush, this));
- }
+ abstract void flush();
/**
* Destroys this session, flushing out all pending notifications to the service.
*
* <p>Once destroyed, any new notification will be dropped.
*/
- public void destroy() {
+ public final void destroy() {
//TODO(b/111276913): mark it as destroyed so other methods are ignored (and test on CTS)
+ //TODO(b/111276913): probably shouldn't check for it
if (!isContentCaptureEnabled()) return;
+ mCloseGuard.close();
+
//TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
// id) and send it to the cache of batched commands
if (VERBOSE) {
- Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
+ Log.v(mTag, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
}
- flush();
+ // Finish children first
+ if (mChildren != null) {
+ final int numberChildren = mChildren.size();
+ if (VERBOSE) Log.v(mTag, "Destroying " + numberChildren + " children first");
+ for (int i = 0; i < numberChildren; i++) {
+ final ContentCaptureSession child = mChildren.get(i);
+ try {
+ child.destroy();
+ } catch (Exception e) {
+ Log.w(mTag, "exception destroying child session #" + i + ": " + e);
+ }
+ }
+ }
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleDestroySession, this));
- mCloseGuard.close();
+ try {
+ flush();
+ } finally {
+ onDestroy();
+ }
}
+ abstract void onDestroy();
+
/** @hide */
@Override
public void close() {
@@ -282,225 +211,6 @@
}
}
- private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
- if (mState != STATE_UNKNOWN) {
- // TODO(b/111276913): revisit this scenario
- Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
- + getStateAsString(mState));
- return;
- }
- mState = STATE_WAITING_FOR_SERVER;
- mApplicationToken = token;
- mComponentName = componentName;
-
- if (VERBOSE) {
- Log.v(TAG, "handleStartSession(): token=" + token + ", act="
- + getActivityDebugName() + ", id=" + mId);
- }
- final int flags = 0; // TODO(b/111276913): get proper flags
-
- try {
- mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
- componentName, mId, mClientContext, flags, new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) {
- IBinder binder = null;
- if (resultData != null) {
- binder = resultData.getBinder(EXTRA_BINDER);
- if (binder == null) {
- Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
- handleResetState();
- return;
- }
- }
- handleSessionStarted(resultCode, binder);
- }
- });
- } catch (RemoteException e) {
- Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
- + e);
- }
- }
-
- /**
- * Callback from {@code system_server} after call to
- * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
- * ContentCaptureContext, int, IResultReceiver)}.
- *
- * @param resultCode session state
- * @param binder handle to {@link IContentCaptureDirectManager}
- */
- private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
- mState = resultCode;
- if (binder != null) {
- mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
- mDirectServiceVulture = () -> {
- Log.w(TAG, "Destroying session " + mId + " because service died");
- destroy();
- };
- try {
- binder.linkToDeath(mDirectServiceVulture, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
- }
- }
- if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
- mDisabled.set(true);
- handleResetSession(/* resetState= */ false);
- } else {
- mDisabled.set(false);
- }
- if (VERBOSE) {
- Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
- + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
- + ", binder=" + binder);
- }
- }
-
- private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
- if (mEvents == null) {
- if (VERBOSE) {
- Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
- }
- mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
- }
- mEvents.add(event);
-
- final int numberEvents = mEvents.size();
-
- // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
- // buffered (either total or per autofillid). For
- // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
- // "a" and "b" then send "abc".
- final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
-
- if (bufferEvent && !forceFlush) {
- handleScheduleFlush(/* checkExisting= */ true);
- return;
- }
-
- if (mState != STATE_ACTIVE) {
- // Callback from startSession hasn't been called yet - typically happens on system
- // apps that are started before the system service
- // TODO(b/111276913): try to ignore session while system is not ready / boot
- // not complete instead. Similarly, the manager service should return right away
- // when the user does not have a service set
- if (VERBOSE) {
- Log.v(TAG, "Closing session for " + getActivityDebugName()
- + " after " + numberEvents + " delayed events and state "
- + getStateAsString(mState));
- }
- handleResetState();
- // TODO(b/111276913): blacklist activity / use special flag to indicate that
- // when it's launched again
- return;
- }
-
- handleForceFlush();
- }
-
- private void handleScheduleFlush(boolean checkExisting) {
- if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
- // "Renew" the flush message by removing the previous one
- mHandler.removeMessages(MSG_FLUSH);
- }
- mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
- if (VERBOSE) {
- Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
- }
- mHandler.sendMessageDelayed(
- obtainMessage(ContentCaptureSession::handleFlushIfNeeded, this).setWhat(MSG_FLUSH),
- FLUSHING_FREQUENCY_MS);
- }
-
- private void handleFlushIfNeeded() {
- if (mEvents.isEmpty()) {
- if (VERBOSE) Log.v(TAG, "Nothing to flush");
- return;
- }
- handleForceFlush();
- }
-
- private void handleForceFlush() {
- if (mEvents == null) return;
-
- if (mDirectServiceInterface == null) {
- Log.w(TAG, "handleForceFlush(): client not available yet");
- if (!mHandler.hasMessages(MSG_FLUSH)) {
- handleScheduleFlush(/* checkExisting= */ false);
- }
- return;
- }
-
- final int numberEvents = mEvents.size();
- try {
- if (DEBUG) {
- Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
- }
- mHandler.removeMessages(MSG_FLUSH);
-
- final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
- mDirectServiceInterface.sendEvents(mId, events);
- } catch (RemoteException e) {
- Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
- + ": " + e);
- }
- }
-
- /**
- * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
- */
- @NonNull
- private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
- // NOTE: we must save a reference to the current mEvents and then set it to to null,
- // otherwise clearing it would clear it in the receiving side if the service is also local.
- final List<ContentCaptureEvent> events = mEvents == null
- ? Collections.emptyList()
- : mEvents;
- mEvents = null;
- return new ParceledListSlice<>(events);
- }
-
- private void handleDestroySession() {
- //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
- // to system_server, so it's ok to call both in sequence here. But once we split
- // them so the events are sent directly to the service, we need to make sure they're
- // sent in order.
- if (DEBUG) {
- Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
- + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
- + getActivityDebugName());
- }
-
- try {
- mSystemServerInterface.finishSession(mContext.getUserId(), mId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error destroying system-service session " + mId + " for "
- + getActivityDebugName() + ": " + e);
- }
- }
-
- private void handleResetState() {
- handleResetSession(/* resetState= */ true);
- }
-
- // TODO(b/111276913): once we support multiple sessions, we might need to move some of these
- // clearings out.
- private void handleResetSession(boolean resetState) {
- if (resetState) {
- mState = STATE_UNKNOWN;
- }
- mContentCaptureSessionId = null;
- mApplicationToken = null;
- mComponentName = null;
- mEvents = null;
- if (mDirectServiceInterface != null) {
- mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
- }
- mDirectServiceInterface = null;
- mHandler.removeMessages(MSG_FLUSH);
- }
-
/**
* Notifies the Content Capture Service that a node has been added to the view structure.
*
@@ -510,7 +220,7 @@
*
* @param node node that has been added.
*/
- public void notifyViewAppeared(@NonNull ViewStructure node) {
+ public final void notifyViewAppeared(@NonNull ViewStructure node) {
Preconditions.checkNotNull(node);
if (!isContentCaptureEnabled()) return;
@@ -518,12 +228,11 @@
throw new IllegalArgumentException("Invalid node class: " + node.getClass());
}
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_APPEARED)
- .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
- /* forceFlush= */ false));
+ internalNotifyViewAppeared((ViewStructureImpl) node);
}
+ abstract void internalNotifyViewAppeared(@NonNull ViewNode.ViewStructureImpl node);
+
/**
* Notifies the Content Capture Service that a node has been removed from the view structure.
*
@@ -532,15 +241,15 @@
*
* @param id id of the node that has been removed.
*/
- public void notifyViewDisappeared(@NonNull AutofillId id) {
+ public final void notifyViewDisappeared(@NonNull AutofillId id) {
Preconditions.checkNotNull(id);
if (!isContentCaptureEnabled()) return;
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
- /* forceFlush= */ false));
+ internalNotifyViewDisappeared(id);
}
+ abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
+
/**
* Notifies the Intelligence Service that the value of a text node has been changed.
*
@@ -549,24 +258,25 @@
* @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
* changed by the user (for example, through the keyboard).
*/
- public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
int flags) {
Preconditions.checkNotNull(id);
if (!isContentCaptureEnabled()) return;
- mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
- .setText(text), /* forceFlush= */ false));
+ internalNotifyViewTextChanged(id, text, flags);
}
+ abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags);
+
/**
* Creates a {@link ViewStructure} for a "standard" view.
*
* @hide
*/
@NonNull
- public ViewStructure newViewStructure(@NonNull View view) {
+ public final ViewStructure newViewStructure(@NonNull View view) {
return new ViewNode.ViewStructureImpl(view);
}
@@ -583,69 +293,25 @@
* @hide
*/
@NonNull
- public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
+ public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
+ int virtualId) {
return new ViewNode.ViewStructureImpl(parentId, virtualId);
}
- private boolean isContentCaptureEnabled() {
- return mSystemServerInterface != null && !mDisabled.get();
- }
+ abstract boolean isContentCaptureEnabled();
+ @CallSuper
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("id: "); pw.println(mId);
- pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
- pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
- if (mSystemServerInterface != null) {
- pw.print(prefix); pw.print("mSystemServerInterface: ");
- pw.println(mSystemServerInterface);
- }
- if (mDirectServiceInterface != null) {
- pw.print(prefix); pw.print("mDirectServiceInterface: ");
- pw.println(mDirectServiceInterface);
- }
- if (mClientContext != null) {
- // NOTE: we don't dump clientContent because it could have PII
- pw.print(prefix); pw.println("hasClientContext");
-
- }
- pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
- pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
- if (mContentCaptureSessionId != null) {
- pw.print(prefix); pw.print("public id: "); pw.println(mContentCaptureSessionId);
- }
- pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
- pw.print(getStateAsString(mState)); pw.println(")");
- if (mApplicationToken != null) {
- pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
- }
- if (mComponentName != null) {
- pw.print(prefix); pw.print("component name: ");
- pw.println(mComponentName.flattenToShortString());
- }
- if (mEvents != null && !mEvents.isEmpty()) {
- final int numberEvents = mEvents.size();
- pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
- pw.print('/'); pw.println(MAX_BUFFER_SIZE);
- if (VERBOSE && numberEvents > 0) {
- final String prefix3 = prefix + " ";
- for (int i = 0; i < numberEvents; i++) {
- final ContentCaptureEvent event = mEvents.get(i);
- pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
- pw.println();
- }
+ if (mChildren != null && !mChildren.isEmpty()) {
+ final String prefix2 = prefix + " ";
+ final int numberChildren = mChildren.size();
+ pw.print(prefix); pw.print("number children: "); pw.print(numberChildren);
+ for (int i = 0; i < numberChildren; i++) {
+ final ContentCaptureSession child = mChildren.get(i);
+ pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
}
- pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
- pw.print(prefix); pw.print("next flush: ");
- TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
}
- }
- /**
- * Gets a string that can be used to identify the activity on logging statements.
- */
- private String getActivityDebugName() {
- return mComponentName == null ? mContext.getPackageName()
- : mComponentName.flattenToShortString();
}
@Override
@@ -653,8 +319,11 @@
return mId;
}
+ /**
+ * @hide
+ */
@NonNull
- private static String getStateAsString(int state) {
+ protected static String getStateAsString(int state) {
switch (state) {
case STATE_UNKNOWN:
return "UNKNOWN";
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
index 145fc16..8d8117b 100644
--- a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -26,5 +26,5 @@
* @hide
*/
oneway interface IContentCaptureDirectManager {
- void sendEvents(in String sessionId, in ParceledListSlice events);
+ void sendEvents(in ParceledListSlice events);
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index cbd3701..01776f8 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -33,7 +33,6 @@
*/
oneway interface IContentCaptureManager {
void startSession(int userId, IBinder activityToken, in ComponentName componentName,
- String sessionId, in ContentCaptureContext clientContext, int flags,
- in IResultReceiver result);
+ String sessionId, int flags, in IResultReceiver result);
void finishSession(int userId, String sessionId);
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
new file mode 100644
index 0000000..ea6f2fe
--- /dev/null
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureManager.DEBUG;
+import static android.view.contentcapture.ContentCaptureManager.VERBOSE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.TimeUtils;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Main session associated with a context.
+ *
+ * <p>This session is created when the activity starts and finished when it stops; clients can use
+ * it to create children activities.
+ *
+ * <p><b>NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread. Hence, the only field that must be thread-safe is {@code mEnabled}, which is called at
+ * the beginning of every method.
+ *
+ * @hide
+ */
+public final class MainContentCaptureSession extends ContentCaptureSession {
+
+ /**
+ * Handler message used to flush the buffer.
+ */
+ private static final int MSG_FLUSH = 1;
+
+ /**
+ * Maximum number of events that are buffered before sent to the app.
+ */
+ // TODO(b/121044064): use settings
+ private static final int MAX_BUFFER_SIZE = 100;
+
+ /**
+ * Frequency the buffer is flushed if stale.
+ */
+ // TODO(b/121044064): use settings
+ private static final int FLUSHING_FREQUENCY_MS = 5_000;
+
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+ * @hide
+ */
+ public static final String EXTRA_BINDER = "binder";
+
+ @NonNull
+ private final AtomicBoolean mDisabled;
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final Handler mHandler;
+
+ /**
+ * Interface to the system_server binder object - it's only used to start the session (and
+ * notify when the session is finished).
+ */
+ @Nullable
+ private final IContentCaptureManager mSystemServerInterface;
+
+ /**
+ * Direct interface to the service binder object - it's used to send the events, including the
+ * last ones (when the session is finished)
+ */
+ @Nullable
+ private IContentCaptureDirectManager mDirectServiceInterface;
+ @Nullable
+ private DeathRecipient mDirectServiceVulture;
+
+ private int mState = STATE_UNKNOWN;
+
+ @Nullable
+ private IBinder mApplicationToken;
+
+ @Nullable
+ private ComponentName mComponentName;
+
+ /**
+ * List of events held to be sent as a batch.
+ */
+ @Nullable
+ private ArrayList<ContentCaptureEvent> mEvents;
+
+ // Used just for debugging purposes (on dump)
+ private long mNextFlush;
+
+ // Lazily created on demand.
+ private ContentCaptureSessionId mContentCaptureSessionId;
+
+ /** @hide */
+ protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
+ @Nullable IContentCaptureManager systemServerInterface,
+ @NonNull AtomicBoolean disabled) {
+ mContext = context;
+ mHandler = handler;
+ mSystemServerInterface = systemServerInterface;
+ mDisabled = disabled;
+ }
+
+ @Override
+ ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
+ final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
+ notifyChildSessionStarted(mId, child.mId, clientContext);
+ return child;
+ }
+
+ /**
+ * Starts this session.
+ *
+ * @hide
+ */
+ void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) {
+ if (!isContentCaptureEnabled()) return;
+
+ if (VERBOSE) {
+ Log.v(mTag, "start(): token=" + applicationToken + ", comp="
+ + ComponentName.flattenToShortString(activityComponent));
+ }
+
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleStartSession, this,
+ applicationToken, activityComponent));
+ }
+
+ @Override
+ void flush() {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleForceFlush, this));
+ }
+
+ @Override
+ void onDestroy() {
+ mHandler.sendMessage(
+ obtainMessage(MainContentCaptureSession::handleDestroySession, this));
+ }
+
+ private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (mState != STATE_UNKNOWN) {
+ // TODO(b/111276913): revisit this scenario
+ Log.w(mTag, "ignoring handleStartSession(" + token + ") while on state "
+ + getStateAsString(mState));
+ return;
+ }
+ mState = STATE_WAITING_FOR_SERVER;
+ mApplicationToken = token;
+ mComponentName = componentName;
+
+ if (VERBOSE) {
+ Log.v(mTag, "handleStartSession(): token=" + token + ", act="
+ + getActivityDebugName() + ", id=" + mId);
+ }
+ final int flags = 0; // TODO(b/111276913): get proper flags
+
+ try {
+ mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
+ componentName, mId, flags, new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ IBinder binder = null;
+ if (resultData != null) {
+ binder = resultData.getBinder(EXTRA_BINDER);
+ if (binder == null) {
+ Log.wtf(mTag, "No " + EXTRA_BINDER + " extra result");
+ handleResetState();
+ return;
+ }
+ }
+ handleSessionStarted(resultCode, binder);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error starting session for " + componentName.flattenToShortString() + ": "
+ + e);
+ }
+ }
+
+ /**
+ * Callback from {@code system_server} after call to
+ * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
+ * ContentCaptureContext, int, IResultReceiver)}.
+ *
+ * @param resultCode session state
+ * @param binder handle to {@code IContentCaptureDirectManager}
+ */
+ private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
+ mState = resultCode;
+ if (binder != null) {
+ mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+ mDirectServiceVulture = () -> {
+ Log.w(mTag, "Destroying session " + mId + " because service died");
+ destroy();
+ };
+ try {
+ binder.linkToDeath(mDirectServiceVulture, 0);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Failed to link to death on " + binder + ": " + e);
+ }
+ }
+ if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+ mDisabled.set(true);
+ handleResetSession(/* resetState= */ false);
+ } else {
+ mDisabled.set(false);
+ }
+ if (VERBOSE) {
+ Log.v(mTag, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+ + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+ + ", binder=" + binder);
+ }
+ }
+
+ private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ if (mEvents == null) {
+ if (VERBOSE) {
+ Log.v(mTag, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+ }
+ mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+ }
+ mEvents.add(event);
+
+ final int numberEvents = mEvents.size();
+
+ // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
+ // buffered (either total or per autofillid). For
+ // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
+ // "a" and "b" then send "abc".
+ final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+
+ if (bufferEvent && !forceFlush) {
+ handleScheduleFlush(/* checkExisting= */ true);
+ return;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ // Callback from startSession hasn't been called yet - typically happens on system
+ // apps that are started before the system service
+ // TODO(b/111276913): try to ignore session while system is not ready / boot
+ // not complete instead. Similarly, the manager service should return right away
+ // when the user does not have a service set
+ if (VERBOSE) {
+ Log.v(mTag, "Closing session for " + getActivityDebugName()
+ + " after " + numberEvents + " delayed events and state "
+ + getStateAsString(mState));
+ }
+ handleResetState();
+ // TODO(b/111276913): blacklist activity / use special flag to indicate that
+ // when it's launched again
+ return;
+ }
+
+ handleForceFlush();
+ }
+
+ private void handleScheduleFlush(boolean checkExisting) {
+ if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
+ // "Renew" the flush message by removing the previous one
+ mHandler.removeMessages(MSG_FLUSH);
+ }
+ mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
+ if (VERBOSE) {
+ Log.v(mTag, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+ }
+ mHandler.sendMessageDelayed(
+ obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this)
+ .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS);
+ }
+
+ private void handleFlushIfNeeded() {
+ if (mEvents.isEmpty()) {
+ if (VERBOSE) Log.v(mTag, "Nothing to flush");
+ return;
+ }
+ handleForceFlush();
+ }
+
+ private void handleForceFlush() {
+ if (mEvents == null) return;
+
+ if (mDirectServiceInterface == null) {
+ if (DEBUG) Log.d(mTag, "handleForceFlush(): hold your horses, client not ready yet!");
+ if (!mHandler.hasMessages(MSG_FLUSH)) {
+ handleScheduleFlush(/* checkExisting= */ false);
+ }
+ return;
+ }
+
+ final int numberEvents = mEvents.size();
+ try {
+ if (DEBUG) {
+ Log.d(mTag, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+ }
+ mHandler.removeMessages(MSG_FLUSH);
+
+ final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
+ mDirectServiceInterface.sendEvents(events);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error sending " + numberEvents + " for " + getActivityDebugName()
+ + ": " + e);
+ }
+ }
+
+ /**
+ * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+ */
+ @NonNull
+ private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
+ // NOTE: we must save a reference to the current mEvents and then set it to to null,
+ // otherwise clearing it would clear it in the receiving side if the service is also local.
+ final List<ContentCaptureEvent> events = mEvents == null
+ ? Collections.emptyList()
+ : mEvents;
+ mEvents = null;
+ return new ParceledListSlice<>(events);
+ }
+
+ private void handleDestroySession() {
+ if (DEBUG) {
+ Log.d(mTag, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ + getActivityDebugName());
+ }
+
+ try {
+ mSystemServerInterface.finishSession(mContext.getUserId(), mId);
+ } catch (RemoteException e) {
+ Log.e(mTag, "Error destroying system-service session " + mId + " for "
+ + getActivityDebugName() + ": " + e);
+ }
+ }
+
+ private void handleResetState() {
+ handleResetSession(/* resetState= */ true);
+ }
+
+ // TODO(b/121033016): once we support multiple sessions, we might need to move some of these
+ // clearings out.
+ private void handleResetSession(boolean resetState) {
+ if (resetState) {
+ mState = STATE_UNKNOWN;
+ }
+
+ // TODO(b/121033016): must reset children (which currently is owned by superclass)
+
+ mContentCaptureSessionId = null;
+ mApplicationToken = null;
+ mComponentName = null;
+ mEvents = null;
+ if (mDirectServiceInterface != null) {
+ mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+ }
+ mDirectServiceInterface = null;
+ mHandler.removeMessages(MSG_FLUSH);
+ }
+
+ @Override
+ void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
+ notifyViewAppeared(mId, node);
+ }
+
+ @Override
+ void internalNotifyViewDisappeared(@NonNull AutofillId id) {
+ notifyViewDisappeared(mId, id);
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags) {
+ notifyViewTextChanged(mId, id, text, flags);
+ }
+
+ @Override
+ boolean isContentCaptureEnabled() {
+ return mSystemServerInterface != null && !mDisabled.get();
+ }
+
+ // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is
+ // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
+ // change should also get get rid of the "internalNotifyXXXX" methods above
+ void notifyChildSessionStarted(@NonNull String parentSessionId,
+ @NonNull String childSessionId, @NonNull ContentCaptureContext clientContext) {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+ .setParentSessionId(parentSessionId)
+ .setClientContext(clientContext),
+ /* forceFlush= */ false));
+ }
+
+ void notifyChildSessionFinished(@NonNull String parentSessionId,
+ @NonNull String childSessionId) {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+ .setParentSessionId(parentSessionId), /* forceFlush= */ false));
+ }
+
+ void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
+ .setViewNode(node.mNode), /* forceFlush= */ false));
+ }
+
+ void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+ /* forceFlush= */ false));
+ }
+
+ void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
+ @Nullable CharSequence text, int flags) {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+ .setText(text), /* forceFlush= */ false));
+ }
+
+ @Override
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("id: "); pw.println(mId);
+ pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
+ if (mSystemServerInterface != null) {
+ pw.print(prefix); pw.print("mSystemServerInterface: ");
+ pw.println(mSystemServerInterface);
+ }
+ if (mDirectServiceInterface != null) {
+ pw.print(prefix); pw.print("mDirectServiceInterface: ");
+ pw.println(mDirectServiceInterface);
+ }
+ pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
+ pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+ if (mContentCaptureSessionId != null) {
+ pw.print(prefix); pw.print("public id: "); pw.println(mContentCaptureSessionId);
+ }
+ pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
+ pw.print(getStateAsString(mState)); pw.println(")");
+ if (mApplicationToken != null) {
+ pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
+ }
+ if (mComponentName != null) {
+ pw.print(prefix); pw.print("component name: ");
+ pw.println(mComponentName.flattenToShortString());
+ }
+ if (mEvents != null && !mEvents.isEmpty()) {
+ final int numberEvents = mEvents.size();
+ pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
+ pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+ if (VERBOSE && numberEvents > 0) {
+ final String prefix3 = prefix + " ";
+ for (int i = 0; i < numberEvents; i++) {
+ final ContentCaptureEvent event = mEvents.get(i);
+ pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+ pw.println();
+ }
+ }
+ pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+ pw.print(prefix); pw.print("next flush: ");
+ TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
+ }
+ super.dump(prefix, pw);
+ }
+
+ /**
+ * Gets a string that can be used to identify the activity on logging statements.
+ */
+ private String getActivityDebugName() {
+ return mComponentName == null ? mContext.getPackageName()
+ : mComponentName.flattenToShortString();
+ }
+}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index b41096c..77cb4cd 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -17,6 +17,7 @@
package android.view.textclassifier;
import android.app.Person;
+import android.content.Context;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -28,7 +29,10 @@
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
+import java.util.StringJoiner;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -84,6 +88,29 @@
new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
}
+ /**
+ * Returns the result id for logging.
+ */
+ public static String createResultId(
+ Context context,
+ List<ConversationActions.Message> messages,
+ int modelVersion,
+ List<Locale> modelLocales) {
+ final StringJoiner localesJoiner = new StringJoiner(",");
+ for (Locale locale : modelLocales) {
+ localesJoiner.add(locale.toLanguageTag());
+ }
+ final String modelName = String.format(
+ Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
+ final int hash = Objects.hash(
+ messages.stream()
+ .map(ConversationActions.Message::getText)
+ .collect(Collectors.toList()),
+ context.getPackageName());
+ return SelectionSessionLogger.SignatureParser.createSignature(
+ SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
+ }
+
private static final class PersonEncoder {
private final Map<Person, Integer> mMapping = new ArrayMap<>();
private int mNextUserId = FIRST_NON_LOCAL_USER;
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index 3bb9ee8..f2fea02 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -27,6 +27,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* A text classifier event.
@@ -498,4 +499,25 @@
}
// TODO: Add build(boolean validate).
}
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder(128);
+ out.append("TextClassifierEvent{");
+ out.append("mEventCategory=").append(mEventCategory);
+ out.append(", mEventType=").append(mEventType);
+ out.append(", mEventContext=").append(mEventContext);
+ out.append(", mResultId=").append(mResultId);
+ out.append(", mEventIndex=").append(mEventIndex);
+ out.append(", mEventTime=").append(mEventTime);
+ out.append(", mExtras=").append(mExtras);
+ out.append(", mRelativeWordStartIndex=").append(mRelativeWordStartIndex);
+ out.append(", mRelativeWordEndIndex=").append(mRelativeWordEndIndex);
+ out.append(", mRelativeSuggestedWordStartIndex=").append(mRelativeSuggestedWordStartIndex);
+ out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex);
+ out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
+ out.append(", mLanguage=").append(mLanguage);
+ out.append("}");
+ return out.toString();
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9b0f9c6..fcd06c3 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -80,6 +80,8 @@
private static final String LOG_TAG = DEFAULT_LOG_TAG;
+ private static final boolean DEBUG = false;
+
private static final File FACTORY_MODEL_DIR = new File("/etc/textclassifier/");
// Annotator
private static final String ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX =
@@ -109,6 +111,8 @@
@GuardedBy("mLock") // Do not access outside this lock.
private LangIdModel mLangIdImpl;
@GuardedBy("mLock") // Do not access outside this lock.
+ private ModelFileManager.ModelFile mActionModelInUse;
+ @GuardedBy("mLock") // Do not access outside this lock.
private ActionsSuggestionsModel mActionsImpl;
private final Object mLoggerLock = new Object();
@@ -342,8 +346,10 @@
}
@Override
- public void onTextClassifierEvent(@NonNull TextClassifierEvent event) {
- // TODO: Implement.
+ public void onTextClassifierEvent(TextClassifierEvent event) {
+ if (DEBUG) {
+ Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
+ }
}
/** @inheritDoc */
@@ -408,7 +414,12 @@
.setConfidenceScore(nativeSuggestion.getScore())
.build());
}
- return new ConversationActions(conversationActions, /*id*/ null);
+ String resultId = ActionsSuggestionsHelper.createResultId(
+ mContext,
+ request.getConversation(),
+ mActionModelInUse.getVersion(),
+ mActionModelInUse.getSupportedLocales());
+ return new ConversationActions(conversationActions, resultId);
} catch (Throwable t) {
// Avoid throwing from this method. Log the error.
Log.e(LOG_TAG, "Error suggesting conversation actions.", t);
@@ -517,6 +528,7 @@
try {
if (pfd != null) {
mActionsImpl = new ActionsSuggestionsModel(pfd.getFd());
+ mActionModelInUse = bestModel;
}
} finally {
maybeCloseAndLogError(pfd);
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 03ff0ca..8a1a0b5 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
private final UserInfo mUserInfo;
private final PackageInfo mPackageInfo;
- public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.P;
+ public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.Q;
public UserPackage(UserInfo user, PackageInfo packageInfo) {
this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index 3fa1b01..e44d6eb 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -26,6 +26,7 @@
abstract class WebSyncManager implements Runnable {
protected static final java.lang.String LOGTAG = "websync";
protected android.webkit.WebViewDatabase mDataBase;
+ @UnsupportedAppUsage
protected android.os.Handler mHandler;
protected WebSyncManager(Context context, String name) {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 46b1f6e..6d88530 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
// visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
/** @hide */
private static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webview.chromium.WebViewChromiumFactoryProviderForP";
+ "com.android.webview.chromium.WebViewChromiumFactoryProviderForQ";
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index 213bb75..c093fe5 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -37,12 +37,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.time.DateTimeException;
-import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
/**
* Controller for managing night display and color mode settings.
@@ -152,28 +148,6 @@
}
/**
- * Returns the time when Night display's activation state last changed, or {@code null} if it
- * has never been changed.
- */
- public LocalDateTime getLastActivatedTime() {
- final ContentResolver cr = mContext.getContentResolver();
- final String lastActivatedTime = Secure.getStringForUser(
- cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
- if (lastActivatedTime != null) {
- try {
- return LocalDateTime.parse(lastActivatedTime);
- } catch (DateTimeParseException ignored) {}
- // Uses the old epoch time.
- try {
- return LocalDateTime.ofInstant(
- Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
- ZoneId.systemDefault());
- } catch (DateTimeException|NumberFormatException ignored) {}
- }
- return null;
- }
-
- /**
* Returns the current auto mode value controlling when Night display will be automatically
* activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
* {@link #AUTO_MODE_TWILIGHT}.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index e571656..e59bee4 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -65,4 +65,6 @@
void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
void stopWatchingNoted(IAppOpsNotedCallback callback);
+
+ int checkOperationRaw(int code, int uid, String packageName);
}
diff --git a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
similarity index 98%
rename from services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
rename to core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
index aaea45e..26cf180 100644
--- a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.infra;
+package com.android.internal.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
diff --git a/services/core/java/com/android/server/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
similarity index 86%
rename from services/core/java/com/android/server/infra/AbstractRemoteService.java
rename to core/java/com/android/internal/infra/AbstractRemoteService.java
index 41dcf89..c94c64a 100644
--- a/services/core/java/com/android/server/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.server.infra;
+package com.android.internal.infra;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -27,13 +28,14 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IInterface;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
-import com.android.server.FgThread;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -61,7 +63,10 @@
//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>,
I extends IInterface> implements DeathRecipient {
- private static final int MSG_UNBIND = 1;
+ private static final int MSG_BIND = 1;
+ private static final int MSG_UNBIND = 2;
+
+ protected static final long PERMANENT_BOUND_TIMEOUT_MS = 0;
protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
@@ -85,6 +90,9 @@
private boolean mServiceDied;
private boolean mCompleted;
+ // Used just for debugging purposes (on dump)
+ private long mNextUnbind;
+
/**
* Callback called when the service dies.
*
@@ -99,7 +107,7 @@
void onServiceDied(T service);
}
- // NOTE: must be package-protected so this class is not extend outside
+ // NOTE: must be package-protected so this class is not extended outside
AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
@NonNull ComponentName componentName, int userId, @NonNull VultureCallback<S> callback,
boolean bindInstantServiceAllowed, boolean verbose) {
@@ -109,7 +117,7 @@
mComponentName = componentName;
mIntent = new Intent(serviceInterface).setComponent(mComponentName);
mUserId = userId;
- mHandler = new Handler(FgThread.getHandler().getLooper());
+ mHandler = new Handler(Looper.getMainLooper());
mBindInstantServiceAllowed = bindInstantServiceAllowed;
}
@@ -155,7 +163,9 @@
protected abstract I getServiceInterface(@NonNull IBinder service);
/**
- * Defines How long after the last interaction with the service we would unbind.
+ * Defines how long after the last interaction with the service we would unbind.
+ *
+ * @return time to unbind (in millis), or {@link #PERMANENT_BOUND_TIMEOUT_MS} to not unbind.
*/
protected abstract long getTimeoutIdleBindMillis();
@@ -164,6 +174,15 @@
*/
protected abstract long getRemoteRequestMillis();
+ /**
+ * Gets the currently registered service interface or {@code null} if the service is not
+ * connected.
+ */
+ @Nullable
+ public final I getServiceInterface() {
+ return mService;
+ }
+
private void handleDestroy() {
if (checkIfDestroyed()) return;
handleOnDestroy();
@@ -210,11 +229,23 @@
.append(mComponentName.flattenToString()).println();
pw.append(prefix).append(tab).append("destroyed=")
.append(String.valueOf(mDestroyed)).println();
+ final boolean bound = handleIsBound();
pw.append(prefix).append(tab).append("bound=")
- .append(String.valueOf(handleIsBound())).println();
+ .append(String.valueOf(bound));
+ final long idleTimeout = getTimeoutIdleBindMillis();
+ if (bound) {
+ if (idleTimeout > 0) {
+ pw.append(" (unbind in : ");
+ TimeUtils.formatDuration(mNextUnbind - SystemClock.elapsedRealtime(), pw);
+ pw.append(")");
+ } else {
+ pw.append(" (permanently bound)");
+ }
+ }
+ pw.println();
pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
pw.append(prefix).append("idleTimeout=")
- .append(Long.toString(getTimeoutIdleBindMillis() / 1000)).append("s").println();
+ .append(Long.toString(idleTimeout / 1000)).append("s").println();
pw.append(prefix).append("requestTimeout=")
.append(Long.toString(getRemoteRequestMillis() / 1000)).append("s").println();
pw.println();
@@ -226,6 +257,8 @@
* <p>This request must be responded by the service somehow (typically using a callback),
* othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
* service doesn't respond.
+ *
+ * <p><b>NOTE: </b>this request is responsible for calling {@link #scheduleUnbind()}.
*/
protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
cancelScheduledUnbind();
@@ -240,7 +273,7 @@
* a simple {@link Runnable}.
*/
protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) {
- cancelScheduledUnbind();
+ scheduleUnbind();
// TODO(b/117779333): fix generics below
@SuppressWarnings({"unchecked", "rawtypes"})
final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
@@ -252,13 +285,41 @@
mHandler.removeMessages(MSG_UNBIND);
}
+ /**
+ * Schedules a request to bind to the remote service.
+ *
+ * <p>Typically used on constructor for implementations that need a permanent connection to
+ * the remote service.
+ */
+ protected void scheduleBind() {
+ if (mHandler.hasMessages(MSG_BIND)) {
+ if (mVerbose) Slog.v(mTag, "scheduleBind(): already scheduled");
+ return;
+ }
+ mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleEnsureBound, this)
+ .setWhat(MSG_BIND));
+ }
+
+ /**
+ * Schedules a request to automatically unbind from the service after the
+ * {@link #getTimeoutIdleBindMillis() idle timeout} expires.
+ */
protected void scheduleUnbind() {
+ final long unbindDelay = getTimeoutIdleBindMillis();
+
+ if (unbindDelay <= 0) {
+ if (mVerbose) Slog.v(mTag, "not scheduling unbind when value is " + unbindDelay);
+ return;
+ }
+
cancelScheduledUnbind();
- // TODO(b/111276913): implement "permanent binding"
// TODO(b/117779333): make sure it's unbound if the service settings changing (right now
// it's not)
+
+ mNextUnbind = SystemClock.elapsedRealtime() + unbindDelay;
+ if (mVerbose) Slog.v(mTag, "unbinding in " + unbindDelay + "ms: " + mNextUnbind);
mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
- .setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
+ .setWhat(MSG_UNBIND), unbindDelay);
}
private void handleUnbind() {
@@ -332,6 +393,7 @@
mService = null;
}
}
+ mNextUnbind = 0;
mContext.unbindService(mServiceConnection);
}
diff --git a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
similarity index 98%
rename from services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
rename to core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
index d32f13b..f0c2233 100644
--- a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.infra;
+package com.android.internal.infra;
import android.annotation.NonNull;
import android.content.ComponentName;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 017da55..cc8da5c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5970,7 +5970,6 @@
} else {
stopAllWifiSignalStrengthTimersLocked(-1);
}
- StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
mWifiSignalStrengthBin = strengthBin;
}
}
@@ -6138,8 +6137,6 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6148,8 +6145,6 @@
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6159,8 +6154,6 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6169,9 +6162,6 @@
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(),
- StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7190,9 +7180,6 @@
WIFI_MULTICAST_ENABLED, mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
}
mWifiMulticastWakelockCount++;
}
@@ -7206,9 +7193,6 @@
mWifiMulticastWakelockCount--;
if (mWifiMulticastWakelockCount == 0) {
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8bdb000..c2c6ae6 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -536,9 +536,11 @@
static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
String libraryPath = System.getProperty("java.library.path");
+ // We use the boot class loader, that's what the runtime expects at AOT.
+ ClassLoader parent = ClassLoader.getSystemClassLoader().getParent();
+
return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
- ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
- null /* classLoaderName */);
+ parent, targetSdkVersion, true /* isNamespaceShared */, null /* classLoaderName */);
}
/**
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index cac2265..240c2e7 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -16,12 +16,12 @@
package com.android.internal.usb;
-import static android.hardware.usb.UsbPort.MODE_AUDIO_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DEBUG_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DFP;
-import static android.hardware.usb.UsbPort.MODE_DUAL;
-import static android.hardware.usb.UsbPort.MODE_NONE;
-import static android.hardware.usb.UsbPort.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
index 27c5834..d215670 100644
--- a/core/java/com/android/internal/widget/NumericTextView.java
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -53,6 +54,7 @@
private OnValueChangedListener mListener;
+ @UnsupportedAppUsage
public NumericTextView(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 21fa75e..dc6a73a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -271,7 +271,7 @@
"libhardware",
"libhardware_legacy",
"libselinux",
- "libicuuc",
+ "libandroidicu",
"libmedia",
"libmediametrics",
"libmeminfo",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 12ca78a..eb7338a 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -45,7 +45,7 @@
class BitmapWrapper {
public:
- BitmapWrapper(Bitmap* bitmap)
+ explicit BitmapWrapper(Bitmap* bitmap)
: mBitmap(bitmap) { }
void freePixels() {
diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h
index 9f6462e..b36b4e6 100644
--- a/core/jni/android/graphics/FontUtils.h
+++ b/core/jni/android/graphics/FontUtils.h
@@ -29,7 +29,7 @@
namespace android {
struct FontFamilyWrapper {
- FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {}
+ explicit FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {}
std::shared_ptr<minikin::FontFamily> family;
};
diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp
index dd99b37..f84a4bd 100644
--- a/core/jni/android/graphics/GIFMovie.cpp
+++ b/core/jni/android/graphics/GIFMovie.cpp
@@ -21,7 +21,7 @@
class GIFMovie : public Movie {
public:
- GIFMovie(SkStream* stream);
+ explicit GIFMovie(SkStream* stream);
virtual ~GIFMovie();
protected:
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 84f53468..fc9ea76 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -102,6 +102,10 @@
return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
}
+static jint saveUnclippedLayer(jlong canvasHandle, jint l, jint t, jint r, jint b) {
+ return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
+}
+
static bool restore(jlong canvasHandle) {
Canvas* canvas = get_canvas(canvasHandle);
if (canvas->getSaveCount() <= 1) {
@@ -651,6 +655,7 @@
{"nSave","(JI)I", (void*) CanvasJNI::save},
{"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
{"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+ {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
{"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
{"nRestore","(J)Z", (void*) CanvasJNI::restore},
{"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 283eb03..29d8f30 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -32,6 +32,7 @@
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include "android_media_AudioEffectDescriptor.h"
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
#include "android_media_MicrophoneInfo.h"
@@ -427,9 +428,14 @@
}
static void
-android_media_AudioSystem_recording_callback(int event, const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
- audio_patch_handle_t patchHandle)
+android_media_AudioSystem_recording_callback(int event,
+ const record_client_info_t *clientInfo,
+ const audio_config_base_t *clientConfig,
+ std::vector<effect_descriptor_t> clientEffects,
+ const audio_config_base_t *deviceConfig,
+ std::vector<effect_descriptor_t> effects __unused,
+ audio_patch_handle_t patchHandle,
+ audio_source_t source)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (env == NULL) {
@@ -460,14 +466,24 @@
recParamData[6] = (jint) patchHandle;
env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData);
+ jobjectArray jClientEffects;
+ convertAudioEffectDescriptorVectorFromNative(env, &jClientEffects, clientEffects);
+
+ jobjectArray jEffects;
+ convertAudioEffectDescriptorVectorFromNative(env, &jEffects, effects);
+
// callback into java
jclass clazz = env->FindClass(kClassPathName);
- env->CallStaticVoidMethod(clazz,
- gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
- event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
- env->DeleteLocalRef(clazz);
+ env->CallStaticVoidMethod(clazz,
+ gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+ event, (jint) clientInfo->uid, clientInfo->session,
+ clientInfo->source, clientInfo->port_id, clientInfo->silenced,
+ recParamArray, jClientEffects, jEffects, source);
+ env->DeleteLocalRef(clazz);
env->DeleteLocalRef(recParamArray);
+ env->DeleteLocalRef(jClientEffects);
+ env->DeleteLocalRef(jEffects);
}
static jint
@@ -2260,7 +2276,7 @@
"dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
- "recordingCallbackFromNative", "(IIII[I)V");
+ "recordingCallbackFromNative", "(IIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V");
jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 516093e..1065738 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -20,7 +20,6 @@
#include "android_media_AudioTrack.h"
#include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
#include "core_jni_helpers.h"
#include <utils/Log.h>
diff --git a/core/jni/android_media_DeviceCallback.cpp b/core/jni/android_media_DeviceCallback.cpp
index 108fa00..a1a0351 100644
--- a/core/jni/android_media_DeviceCallback.cpp
+++ b/core/jni/android_media_DeviceCallback.cpp
@@ -20,7 +20,6 @@
#include <utils/Log.h>
#include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
#include "core_jni_helpers.h"
#include <media/AudioSystem.h>
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index f6e5c7a..c33405d 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -20,8 +20,9 @@
#include <cutils/ashmem.h>
#include <utils/Log.h>
+
+#include <nativehelper/jni_macros.h>
#include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
#include <nativehelper/ScopedLocalRef.h>
#include <algorithm>
@@ -31,10 +32,10 @@
namespace {
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass,
- "<init>", "(Ljava/lang/String;I)V");
+jclass errnoExceptionClass;
+jmethodID errnoExceptionCtor; // MethodID for ErrnoException.<init>(String,I)
+void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
if (detailMessage.get() == NULL) {
// Not really much we can do here. We're probably dead in the water,
@@ -42,12 +43,14 @@
env->ExceptionClear();
}
- jobject exception = env->NewObject(JniConstants::errnoExceptionClass, ctor,
- detailMessage.get(), error);
+ jobject exception = env->NewObject(errnoExceptionClass,
+ errnoExceptionCtor,
+ detailMessage.get(),
+ error);
env->Throw(reinterpret_cast<jthrowable>(exception));
}
-static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) {
+jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {
// Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
const char* name = jname ? env->GetStringUTFChars(jname, nullptr) : nullptr;
@@ -69,7 +72,7 @@
return jniCreateFileDescriptor(env, fd);
}
-static jint SharedMemory_getSize(JNIEnv* env, jobject, jobject fileDescriptor) {
+jint SharedMemory_nGetSize(JNIEnv* env, jobject, jobject fileDescriptor) {
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (!ashmem_valid(fd)) {
return -1;
@@ -78,7 +81,7 @@
return static_cast<jint>(std::min(size, static_cast<size_t>(std::numeric_limits<jint>::max())));
}
-static jint SharedMemory_setProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) {
+jint SharedMemory_nSetProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) {
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
int err = 0;
if (ashmem_set_prot_region(fd, prot)) {
@@ -87,18 +90,21 @@
return err;
}
-static const JNINativeMethod methods[] = {
- {"nCreate", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)SharedMemory_create},
- {"nGetSize", "(Ljava/io/FileDescriptor;)I", (void*)SharedMemory_getSize},
- {"nSetProt", "(Ljava/io/FileDescriptor;I)I", (void*)SharedMemory_setProt},
+const JNINativeMethod methods[] = {
+ NATIVE_METHOD(SharedMemory, nCreate, "(Ljava/lang/String;I)Ljava/io/FileDescriptor;"),
+ NATIVE_METHOD(SharedMemory, nGetSize, "(Ljava/io/FileDescriptor;)I"),
+ NATIVE_METHOD(SharedMemory, nSetProt, "(Ljava/io/FileDescriptor;I)I")
};
} // anonymous namespace
namespace android {
-int register_android_os_SharedMemory(JNIEnv* env)
-{
+int register_android_os_SharedMemory(JNIEnv* env) {
+ errnoExceptionClass =
+ MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
+ errnoExceptionCtor =
+ GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V");
return RegisterMethodsOrDie(env, "android/os/SharedMemory", methods, NELEM(methods));
}
diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp
index 4ab8db4..182a621 100644
--- a/core/jni/android_util_jar_StrictJarFile.cpp
+++ b/core/jni/android_util_jar_StrictJarFile.cpp
@@ -22,24 +22,26 @@
#include <log/log.h>
+#include <nativehelper/jni_macros.h>
#include <nativehelper/JNIHelp.h>
-#include <nativehelper/JniConstants.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedUtfChars.h>
-#include "jni.h"
+
+#include "core_jni_helpers.h"
#include "ziparchive/zip_archive.h"
-namespace android {
+namespace {
+jclass zipEntryClass;
// The method ID for ZipEntry.<init>(String,String,JJJIII[BJJ)
-static jmethodID zipEntryCtor;
+jmethodID zipEntryCtor;
-static void throwIoException(JNIEnv* env, const int32_t errorCode) {
+void throwIoException(JNIEnv* env, const int32_t errorCode) {
jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode));
}
-static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
- return env->NewObject(JniConstants::zipEntryClass,
+jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
+ return env->NewObject(zipEntryClass,
zipEntryCtor,
entryName,
NULL, // comment
@@ -52,7 +54,7 @@
static_cast<jlong>(entry.offset));
}
-static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) {
+jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) {
// Name argument is used for logging, and can be any string.
ScopedUtfChars nameChars(env, name);
if (nameChars.c_str() == NULL) {
@@ -90,7 +92,7 @@
};
-static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle,
+jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle,
jstring prefix) {
ScopedUtfChars prefixChars(env, prefix);
if (prefixChars.c_str() == NULL) {
@@ -116,7 +118,7 @@
return reinterpret_cast<jlong>(handle);
}
-static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
+jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
ZipEntry data;
ZipString entryName;
@@ -135,7 +137,7 @@
return newZipEntry(env, data, entryNameString.get());
}
-static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
+jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
jstring entryName) {
ScopedUtfChars entryNameChars(env, entryName);
if (entryNameChars.c_str() == NULL) {
@@ -152,11 +154,11 @@
return newZipEntry(env, data, entryName);
}
-static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
+void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle));
}
-static JNINativeMethod gMethods[] = {
+JNINativeMethod gMethods[] = {
NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;I)J"),
NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"),
NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"),
@@ -164,14 +166,15 @@
NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"),
};
+} // namespace
+
+namespace android {
+
int register_android_util_jar_StrictJarFile(JNIEnv* env) {
- jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
-
- zipEntryCtor = env->GetMethodID(JniConstants::zipEntryClass, "<init>",
- "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V");
- LOG_ALWAYS_FATAL_IF(zipEntryCtor == NULL, "Unable to find ZipEntry.<init>");
-
- return 0;
+ zipEntryClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/util/zip/ZipEntry"));
+ zipEntryCtor = GetMethodIDOrDie(env, zipEntryClass, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V");
+ return jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
}
}; // namespace android
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 33b2689..bd87dcc 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -152,7 +152,7 @@
const bool is_sock;
private:
- FileDescriptorInfo(int fd);
+ explicit FileDescriptorInfo(int fd);
FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
int fd_flags, int fs_flags, off_t offset);
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index a3570d7..09022a2 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -86,7 +86,7 @@
bool ReopenOrDetach(std::string* error_msg);
private:
- FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
+ explicit FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
bool RestatInternal(std::set<int>& open_fds, std::string* error_msg);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cc8927f..c6343a8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -611,6 +611,7 @@
<protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
<!-- Added in Q -->
+ <protected-broadcast android:name="android.content.pm.action.SESSION_UPDATED" />
<!-- For CarIdlenessTracker -->
<protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" />
@@ -2003,6 +2004,15 @@
<permission android:name="android.permission.BIND_SCREENING_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by a {@link android.telecom.PhoneAccountSuggestionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a {@link android.telecom.CallRedirectionService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
@@ -4327,7 +4337,7 @@
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
- <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use
+ <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#Q} that want to use
{@link android.app.Notification.Builder#setFullScreenIntent notification full screen
intents}. -->
<permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
deleted file mode 100644
index 606b686..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
- android:fillColor="#FFFFFF"
- android:fillAlpha="0.3" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml b/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
deleted file mode 100644
index a4c5bc2..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
- android:fillColor="#FFFFFF"
- android:fillAlpha="0.3" />
- <path
- android:pathData="M-377 308.5c0 -0.20001 0 -0.29999 0.10001 -0.5 0 0 0 0 -0.10001 0 -2.10001 0 -3.60001 1.20001 -3.79999 1.29999L-377 314l1.60001 -2.10001c-0.9 -0.79998 -1.60001 -2 -1.60001 -3.39999z"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
deleted file mode 100644
index 9c27833..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
- android:fillColor="#FFFFFF"
- android:fillAlpha="0.3" />
- <path
- android:pathData="M-377 308.5c0 -0.89999 0.29999 -1.70001 0.70001 -2.5 -0.20001 0 -0.5 0 -0.70001 0 -2.79999 0 -4.79999 1.60001 -5 1.79999l5 6.20001 0 0 0 0 1.60001 -2c-1 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
deleted file mode 100644
index 6d693f1..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
- android:fillColor="#FFFFFF"
- android:fillAlpha="0.3" />
- <path
- android:pathData="M-375.39999 311.89999c-1 -0.79998 -1.60001 -2.1 -1.60001 -3.39999 0 -1.79999 1.10001 -3.39999 2.70001 -4.10001C-375.09998 304.19998 -376 304 -377 304c-3.60001 0 -6 1.89999 -6.29999 2.20001L-377 314l0 0 0 0"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
deleted file mode 100644
index c48fa36..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-377 308.5c0 -2.5 2 -4.5 4.5 -4.5l3.5 0 0.79999 -1c-0.29999 -0.29999 -3.70001 -3 -8.79999 -3 -5.09998 0 -8.5 2.79999 -8.79999 3l8.79999 11 0 0 0 0 1.60001 -2c-0.9 -0.89999 -1.60001 -2.10001 -1.60001 -3.5z"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4k.xml b/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
deleted file mode 100644
index 0868845..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-373.04999 308.79999l0.5 0 0 0.89999 -0.5 0 0 1.20001 -1.1 0 0 -1.20001 -1.9 0 -0.1 -0.70001 1.89999 -3.70001 1.10001 0 0 3.50003 0.1 0zm-1.89999 0l0.89999 0 0 -1.9 0 0 -0.89999 1.9z"
- android:fillColor="#FFFFFF" />
- <path
- android:pathData="M-370.44998 308.70001l-0.5 0.60001 0 1.70001 -1.10001 0 0 -5.70001 1.10001 0 0 2.5 0.39999 -0.60001 1.10001 -1.89999 1.39999 0 -1.6 2.5 1.70001 3.20001 -1.29999 0 -1.20001 -2.30002z"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_hd.xml b/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
deleted file mode 100644
index 657f5ed..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-371.79999 311l-1.1 0 0 -2.29999 -0.79999 0 0 2.29999 -1.10001 0 0 -5.70001 1.10001 0 0 2.39999 0.79999 0 0 -2.39999 1.1 0 0 5.70001z"
- android:fillColor="#FFFFFF" />
- <path
- android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_ld.xml b/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
deleted file mode 100644
index e2971aa..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
- android:fillColor="#FFFFFF" />
- <path
- android:pathData="M-373.13333 310.13333l1.33334 0 0 0.86667 -2.46667 0 0 -5.66666 1.13333 0 0 4.79999z"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_sd.xml b/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
deleted file mode 100644
index b073be3..0000000
--- a/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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:viewportWidth="18"
- android:viewportHeight="18"
- android:width="26dp"
- android:height="24dp">
- <group
- android:translateX="386"
- android:translateY="-298">
- <path
- android:pathData="M-371.33557 310.98651l0 -5.68701 1.39068 0c0.27848 0 0.53336 0.0532 0.76465 0.16016 0.2313 0.10693 0.42954 0.2622 0.59568 0.46679 0.16519 0.20459 0.29357 0.45557 0.38421 0.75391 0.0906 0.29834 0.13593 0.63867 0.13593 1.02148l0 0.88672c0 0.38281 -0.0453 0.72363 -0.13593 1.021 -0.0906 0.29785 -0.21902 0.5498 -0.38421 0.7539 -0.16614 0.20411 -0.36628 0.35938 -0.59946 0.46485 -0.23316 0.10547 -0.49182 0.1582 -0.77786 0.1582l-1.37369 0zm1.06879 -4.76904l0 3.85107 0.26333 0c0.15491 0 0.28452 -0.0283 0.38971 -0.084 0.10516 -0.0557 0.19077 -0.14356 0.25681 -0.26367 0.066 -0.12012 0.11331 -0.27198 0.14184 -0.4585 0.0285 -0.18652 0.0424 -0.41113 0.0424 -0.67383l0 -0.89453c0 -0.26562 -0.0138 -0.49219 -0.0424 -0.67969 -0.0285 -0.1875 -0.0758 -0.33984 -0.14102 -0.45703 -0.0644 -0.11719 -0.1492 -0.20312 -0.25275 -0.25781 -0.10437 -0.0547 -0.23071 -0.082 -0.37991 -0.082l-0.27801 0z"
- android:fillColor="#FFFFFF" />
- <path
- android:pathData="M-372.87598 309.47461c0 -0.10645 -0.01 -0.20117 -0.0303 -0.28223 -0.0205 -0.0811 -0.0576 -0.15527 -0.11035 -0.22265 -0.0537 -0.0674 -0.12598 -0.12891 -0.21777 -0.18457 -0.0908 -0.0566 -0.20704 -0.11231 -0.34668 -0.16797 -0.24903 -0.0889 -0.47657 -0.18457 -0.68165 -0.28614 -0.20605 -0.10156 -0.38281 -0.2207 -0.53027 -0.35839 -0.14746 -0.13721 -0.26172 -0.29639 -0.34277 -0.47803 -0.0811 -0.18164 -0.12207 -0.39697 -0.12207 -0.646 0 -0.23144 0.042 -0.4414 0.12793 -0.63086 0.085 -0.18945 0.20312 -0.35205 0.35644 -0.48779 0.15235 -0.13623 0.33496 -0.2417 0.54883 -0.31641 0.21289 -0.0752 0.44824 -0.1123 0.70508 -0.1123 0.2666 0 0.50683 0.0425 0.71973 0.12744 0.21386 0.0854 0.39648 0.2041 0.54687 0.35645 0.15137 0.15234 0.26758 0.333 0.34766 0.54101 0.0791 0.2085 0.11914 0.43604 0.11914 0.68262l-1.07422 0c0 -0.11963 -0.0127 -0.22998 -0.0381 -0.33154 -0.0254 -0.10205 -0.0654 -0.18897 -0.12011 -0.26123 -0.0547 -0.0723 -0.125 -0.12891 -0.20997 -0.16993 -0.085 -0.0405 -0.1875 -0.0605 -0.30664 -0.0605 -0.11132 0 -0.208 0.0171 -0.29004 0.0513 -0.0811 0.0342 -0.14843 0.0815 -0.20117 0.14111 -0.0537 0.0596 -0.0928 0.13037 -0.11816 0.21143 -0.0254 0.0815 -0.0381 0.16894 -0.0381 0.26318 0 0.0937 0.0166 0.17725 0.0508 0.24951 0.0342 0.0723 0.0869 0.14014 0.15625 0.20361 0.0703 0.064 0.16016 0.125 0.26856 0.18311 0.10937 0.0586 0.23926 0.11963 0.38965 0.1831 0.24316 0.084 0.46093 0.17823 0.65136 0.2837 0.19043 0.10498 0.35059 0.22998 0.48047 0.37158 0.12891 0.14258 0.22754 0.30762 0.29493 0.49316 0.0674 0.1875 0.10156 0.40235 0.10156 0.64649 0 0.24121 -0.04 0.458 -0.12012 0.64843 -0.0801 0.19043 -0.19434 0.35059 -0.3418 0.48145 -0.14746 0.13086 -0.32617 0.23144 -0.53711 0.30176 -0.21093 0.0693 -0.44726 0.10449 -0.70898 0.10449 -0.23633 0 -0.46777 -0.0361 -0.69531 -0.1084 -0.22754 -0.0723 -0.43067 -0.18359 -0.61035 -0.33203 -0.17872 -0.14844 -0.32325 -0.33691 -0.43262 -0.56543 -0.10938 -0.22852 -0.16309 -0.49805 -0.16309 -0.80859l1.07813 0c0 0.17089 0.0166 0.31445 0.0498 0.43261 0.0332 0.11817 0.084 0.21485 0.15235 0.29004 0.0684 0.0752 0.15429 0.12891 0.25683 0.16211 0.10352 0.0332 0.22461 0.0488 0.36426 0.0488 0.11719 0 0.21582 -0.0156 0.2959 -0.0488 0.0801 -0.0332 0.14355 -0.0781 0.19238 -0.13574 0.0478 -0.0566 0.082 -0.125 0.10254 -0.2041 0.0205 -0.0781 0.0303 -0.16504 0.0303 -0.25879z"
- android:fillColor="#FFFFFF" />
- </group>
-</vector>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 266e846..3f23da6 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"roeteer oproepe deur die stelsel"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Laat die program toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"sien en beheer oproepe deur die stelsel."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Laat die program toe om deurlopende oproepe op die toestel te sien en te beheer. Dit sluit inligting in soos oproepnommers vir oproepe en die toedrag van die oproepe."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"gaan voort met \'n oproep uit \'n ander program"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Laat die program toe om \'n oproep voort te sit wat in \'n ander program begin is."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lees foonnommers"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Laat die program toe om met kortveldkommunikasie- (NFC) merkers, kaarte en lesers te kommunikeer."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktiveer jou skermslot"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Laat die program toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en atkiveer dit dan weer wanneer die oproep eindig."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"kry en versoek skermslotkompleksiteit"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Laat die program toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie program kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak toe opdateer, maar gebruikers kan dit vrylik ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie sodat die program nie die presiese wagwoord ken nie."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"gebruik biometriese hardeware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Laat die program toe om biometriese hardeware vir stawing te gebruik"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"bestuur vingerafdrukhardeware"</string>
@@ -1086,7 +1090,7 @@
<string name="email" msgid="4560673117055050403">"E-pos"</string>
<string name="email_desc" msgid="3638665569546416795">"Stuur e-pos aan gekose adres"</string>
<string name="dial" msgid="1253998302767701559">"Bel"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Bel gekose foonnommer"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Bel dié foonnommer"</string>
<string name="map" msgid="5441053548030107189">"Kaart"</string>
<string name="map_desc" msgid="1836995341943772348">"Soek geselekteerde adres"</string>
<string name="browse" msgid="1245903488306147205">"Maak oop"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet dateer tans oop …"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Toestel dateer tans op …"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Foon begin tans …"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android begin tans …"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet begin tans …"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Toestel begin tans …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimeer tans berging."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tik om alle netwerke te sien"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Koppel"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle netwerke"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Gekoppel aan Wi‑Fi-netwerk wat voorgestel is deur <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Wil jy <xliff:g id="NAME">%s</xliff:g> toelaat om netwerke vir jou voor te stel?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nee"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi sal outomaties aanskakel"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wanneer jy naby \'n gestoorde hoëgehaltenetwerk is"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Moenie weer aanskakel nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f3b4df7..ca1aabd 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"መተግበሪያው የመሳሪያውን የስልክ ባህሪያት ላይ እንዲደርስ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው የስልክ ቁጥሩን እና የመሳሪያውን መታወቂያዎች፣ ጥሪ የነቃ እንደሆነ፣ እና በጥሪ የተገናኘውን የሩቅ ቁጥር እንዲወስን ይፈቅድለታል።"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ጥሪዎችን በስርዓቱ በኩል አዙር"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"መተግበሪያው የጥሪ ተሞክሮን እንዲያሻሽል ጥሪዎቹን በስርዓቱ በኩል እንዲያዞር ያስችለዋል።"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"በሥርዓቱ በኩል ጥሪዎችን ይመልከቱ እና ይቆጣጠሩ።"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"መተግበሪያው በመሣሪያው ላይ በመካሄድ ላይ ያሉ ጥሪዎችን እንዲመለከት እና እንዲቆጣጠር ይፈቅድለታል። ይህ ለጥሪዎች እንደ የጥሪ ቁጥሮች እና የጥሪዎች ሁኔታ የመሰለ መረጃን ያካትታል።"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"በሌላ መተግበሪያ የተጀመረ ጥሪን መቀጠል"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"መተግበሪያው በሌላ መተግበሪያ ውስጥ የተጀመረ ጥሪ እንዲቀጥል ያስችለዋል።"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ስልክ ቁጥሮች ያንብቡ"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ከቅርብ ግኑኙነት መስክ (NFC) መለያዎች፣ ካርዶች እና አንባቢ ጋር ለማገናኘት ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"የማያ ገጽዎን መቆለፊያ ያሰናክሉ"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"መተግበሪያው መቆለፊያውና ማንኛውም የተጎዳኘ የይለፍ ቃል ደህንነት እንዲያሰናክል ይፈቅድለታል። ለምሳሌ ስልኩ ገቢ የስልክ ጥሪ በሚቀበልበት ጊዜ መቆለፊያውን ያሰናክልና ከዚያም ጥሪው ሲጠናቀቅ መቆለፊያውን በድጋሚ ያነቃዋል።"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"የማያ ገጽ መቆለፊያ ውስብስብነት ያግኙ እና ይጠይቁ"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"መተግበሪያው የማያ ገጽ መቆለፊያው ውስብስብነት ደረጃ (ከፍተኛ፣ መካከለኛ፣ ዝቅተኛ ወይም ምንም) እንዲያውቅ ያስችለዋል፣ ይህም ሊሆኑ የሚችለው የማያ ገጽ መቆለፊያው ርዝመት እና አይነት ክልል ያመለክታል። መተግበሪያው እንዲሁም ለተጠቃሚዎች የማያ ገጽ መቆለፊያውን ወደተወሰነ ደረጃ እንዲያዘምኑት ሊጠቁማቸው ይችላል። የማያ ገጽ መቆለፊያው በስነጣ አልባ ጽሑፍ እንደማይከማች ልብ ይበሉ፣ በዚህም መተግበሪያው ትክክለኛውን የይለፍ ቃል አያውቅም።"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ባዮሜትራዊ ሃርድዌርን መጠቀም"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"መተግበሪያው የባዮሜትራዊ ሃርድዌር ለማረጋገጥ ስራ እንዲጠቀም ያስችለዋል"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"የጣት አሻራ ሃርድዌርን አስተዳድር"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ጡባዊ በመዘመን ላይ ነው…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"መሣሪያ በመዘመን ላይ ነው…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ስልክ በመጀመር ላይ ነው…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android በመጀመር ላይ ነው…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ጡባዊ በመጀመር ላይ ነው…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"መሣሪያ በመጀመር ላይ ነው…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ማከማቻን በማመቻቸት ላይ።"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ሁሉንም አውታረ መረቦችን ለማየት መታ ያድርጉ"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"አገናኝ"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ሁሉም አውታረ መረቦች"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"በ<xliff:g id="NAME">%s</xliff:g> ከተጠቆመው Wi-Fi ጋር ተገናኝቷል"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> አውታረ መረቦችን ለእርስዎ እንዲጠቁሙ ይፈልጋሉ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"አዎ"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"አይ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi በራስ-ሰር ይበራል"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ከፍተኛ ጥራት ያለው የተቀመጠ አውታረ መረብ አቅራቢያ ሲሆኑ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"መልሰህ አታብራ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0789013..4557563 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -313,7 +313,7 @@
<string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"هل تريد السماح للتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالتعرّف على نشاطك البدني؟"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"الكاميرا"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"التقاط صور وتسجيل فيديو"</string>
- <string name="permgrouprequest_camera" msgid="1299833592069671756">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالتقاط الصور وتسجيل الفيديو؟"</string>
+ <string name="permgrouprequest_camera" msgid="1299833592069671756">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالتقاط صور وتسجيل فيديو؟"</string>
<string name="permgrouplab_calllog" msgid="8798646184930388160">"سجلّ المكالمات"</string>
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"قراءة سجلّ المكالمات الهاتفية والكتابة إليه"</string>
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالدخول إلى سجلات مكالماتك الهاتفية؟"</string>
@@ -459,6 +459,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"للسماح للتطبيق بالدخول إلى ميزات الهاتف في الجهاز. ويتيح هذا الإذن للتطبيق تحديد رقم الهاتف ومعرّفات الجهاز، وما إذا كانت هناك مكالمة نشطة والرقم البعيد الذي تم الاتصال به في المكالمة."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"توجيه المكالمات من خلال النظام"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"يسمح للتطبيق بتوجيه المكالمات من خلال النظام لتحسين تجربة الاتصال."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"رؤية المكالمات والتحكّم فيها من خلال النظام"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"يستطيع التطبيق رؤية المكالمات الجارية على الجهاز والتحكّم فيها. ويشمل ذلك معلومات مثل الأرقام وحالة المكالمات."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"مواصلة مكالمة من تطبيق آخر"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"السماح للتطبيق بمواصلة مكالمة بدأت في تطبيق آخر."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"قراءة أرقام الهواتف"</string>
@@ -519,6 +521,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"للسماح للتطبيق بالاتصال بعلامات الاتصال قريب المدى (NFC)، والبطاقات وبرامج القراءة."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"إيقاف قفل الشاشة"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"للسماح للتطبيق بإيقاف تأمين المفاتيح وأي أمان لكلمة مرور مرتبطة. على سبيل المثال، يعطل الهاتف تأمين المفاتيح عند استقبال مكالمة هاتفية واردة، ثم يعيد تفعيل تأمين المفاتيح عند انتهاء المكالمة."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"طلب قفل شاشة صعب والحصول عليه"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"للسماح للتطبيق بمعرفة مستوى صعوبة قفل الشاشة (عالي أو متوسط أو منخفض الصعوبة أو بدون)، والذي يحدّد النطاق المحتمل لطول ونوع قفل الشاشة. ويمكن أن يقترح التطبيق للمستخدمين أيضًا تعديل قفل الشاشة إلى مستوى معيّن، ولهم مطلق الحرية في تجاهل هذا الاقتراح ورفضه. وتجدر الإشارة إلى أنه لا يتم حفظ قفل الشاشة في نص عادي، لذا لا يعرف التطبيق كلمة المرور تحديدًا."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"استخدام الأجهزة البيومترية"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"للسماح للتطبيق باستخدام الأجهزة البيومترية للمصادقة"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"لإدارة أجهزة بصمة الإصبع"</string>
@@ -1163,15 +1167,15 @@
<string name="deleteText" msgid="6979668428458199034">"حذف"</string>
<string name="inputMethod" msgid="1653630062304567879">"طريقة الإرسال"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"إجراءات النص"</string>
- <string name="email" msgid="4560673117055050403">"بريد إلكتروني"</string>
+ <string name="email" msgid="4560673117055050403">"إرسال بريد إلكتروني"</string>
<string name="email_desc" msgid="3638665569546416795">"مراسلة العنوان المختار عبر البريد الإلكتروني"</string>
<string name="dial" msgid="1253998302767701559">"اتصال"</string>
<string name="dial_desc" msgid="6573723404985517250">"الاتصال برقم الهاتف المختار"</string>
- <string name="map" msgid="5441053548030107189">"خريطة"</string>
+ <string name="map" msgid="5441053548030107189">"فتح تطبيق خرائط"</string>
<string name="map_desc" msgid="1836995341943772348">"تحديد موقع العنوان المختار"</string>
<string name="browse" msgid="1245903488306147205">"فتح"</string>
<string name="browse_desc" msgid="8220976549618935044">"فتح عنوان URL المختار"</string>
- <string name="sms" msgid="4560537514610063430">"رسالة"</string>
+ <string name="sms" msgid="4560537514610063430">"إرسال رسائل قصيرة"</string>
<string name="sms_desc" msgid="7526588350969638809">"مراسلة رقم الهاتف المختار"</string>
<string name="add_contact" msgid="7867066569670597203">"إضافة"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"إضافة إلى جهات الاتصال"</string>
@@ -1261,6 +1265,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"جارٍ تحديث الجهاز اللوحي…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"جارٍ تحديث الجهاز…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"جارٍ بدء تشغيل الهاتف…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"جارٍ تشغيل Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"جارٍ بدء تشغيل الجهاز اللوحي…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"جارٍ بدء تشغيل الجهاز…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين مساحة التخزين."</string>
@@ -1327,6 +1332,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"انقر للاطلاع على جميع الشبكات"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"اتصال"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"جميع الشبكات"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"تم الاتصال بشبكة Wi‑Fi التي اقترحها <xliff:g id="NAME">%s</xliff:g>."</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"هل تريد السماح لـ <xliff:g id="NAME">%s</xliff:g> باقتراح شبكات لك؟"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"نعم"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"لا"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"سيتم تشغيل شبكة Wi-Fi تلقائيًا."</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"عندما تكون بالقرب من شبكة محفوظة عالية الجودة"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"عدم إعادة التشغيل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f716579..a4f950a 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -35,10 +35,10 @@
<string name="mmiError" msgid="5154499457739052907">"সংযোগৰ সমস্যা বা MMI ক\'ড মান্য নহয়।"</string>
<string name="mmiFdnError" msgid="5224398216385316471">"কেৱল ফিক্সড ডায়েলিং নম্বৰৰ বাবে কার্য সীমাবদ্ধ কৰা হৈছে।"</string>
<string name="mmiErrorWhileRoaming" msgid="762488890299284230">"আপুনি ৰ\'মিঙত থকাৰ সময়ত কল ফৰৱাৰ্ডিঙৰ ছেটিংসমূহ সলনি কৰিব নোৱাৰি।"</string>
- <string name="serviceEnabled" msgid="8147278346414714315">"সেৱা সক্ষম কৰা হ\'ল।"</string>
- <string name="serviceEnabledFor" msgid="6856228140453471041">"সেৱা সক্ষম কৰা হ\'ল:"</string>
- <string name="serviceDisabled" msgid="1937553226592516411">"সেৱা অক্ষম কৰা হ\'ল।"</string>
- <string name="serviceRegistered" msgid="6275019082598102493">"পঞ্জীকৰণ সফল হ\'ল।"</string>
+ <string name="serviceEnabled" msgid="8147278346414714315">"সেৱা সক্ষম কৰা হ’ল।"</string>
+ <string name="serviceEnabledFor" msgid="6856228140453471041">"সেৱা সক্ষম কৰা হ’ল:"</string>
+ <string name="serviceDisabled" msgid="1937553226592516411">"সেৱা অক্ষম কৰা হ’ল।"</string>
+ <string name="serviceRegistered" msgid="6275019082598102493">"পঞ্জীকৰণ সফল হ’ল।"</string>
<string name="serviceErased" msgid="1288584695297200972">"সফলভাৱে মচা হ\'ল৷"</string>
<string name="passwordIncorrect" msgid="7612208839450128715">"ভুল পাছৱৰ্ড৷"</string>
<string name="mmiComplete" msgid="8232527495411698359">"MMI সম্পূৰ্ণ হ’ল।"</string>
@@ -66,7 +66,7 @@
<string name="PwdMmi" msgid="7043715687905254199">"পাছৱর্ড সলনি কৰা"</string>
<string name="PinMmi" msgid="3113117780361190304">"পিন সলনি কৰা"</string>
<string name="CnipMmi" msgid="3110534680557857162">"বর্তমান কল কৰা নম্বৰ"</string>
- <string name="CnirMmi" msgid="3062102121430548731">"বর্তমান কল কৰা নম্বৰ সীমিত কৰা হ\'ল"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"বর্তমান কল কৰা নম্বৰ সীমিত কৰা হ’ল"</string>
<string name="ThreeWCMmi" msgid="9051047170321190368">"ত্ৰিপক্ষীয় কলিং"</string>
<string name="RuacMmi" msgid="7827887459138308886">"আমনিদায়ক কল প্ৰত্যাখ্যান"</string>
<string name="CndMmi" msgid="3116446237081575808">"কল কৰা নম্বৰত ডেলিভাৰী"</string>
@@ -77,20 +77,20 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"সুবিধা যোগান ধৰা হোৱা নাই।"</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"আপুনি কলাৰ আইডি ছেটিং সলনি কৰিব নোৱাৰে।"</string>
- <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"কোনো ম\'বাইল ডেটা সেৱা নাই"</string>
+ <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"কোনো ম’বাইল ডেটা সেৱা নাই"</string>
<string name="RestrictedOnEmergencyTitle" msgid="6855466023161191166">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"কোনো ভইচ সেৱা নাই"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="8037246983606545202">"ধ্বনি সেৱা বা জৰুৰীকালীন কলৰ সেৱা উপলব্ধ নহয়"</string>
<string name="RestrictedStateContent" msgid="6538703255570997248">"আপোনাৰ বাহকে সাময়িকভাৱে অফ কৰি থৈছে"</string>
<string name="RestrictedStateContentMsimTemplate" msgid="673416791370248176">"আপোনাৰ বাহকে <xliff:g id="SIMNUMBER">%d</xliff:g> ছিমৰ বাবে সাময়িকভাৱে অফ কৰিছে"</string>
- <string name="NetworkPreferenceSwitchTitle" msgid="6982395015324165258">"ম\'বাইল নেটৱৰ্কৰ লগত সংযোগ কৰিব পৰা নাই"</string>
+ <string name="NetworkPreferenceSwitchTitle" msgid="6982395015324165258">"ম’বাইল নেটৱৰ্কৰ লগত সংযোগ কৰিব পৰা নাই"</string>
<string name="NetworkPreferenceSwitchSummary" msgid="509327194863482733">"পচন্দৰ নেটৱৰ্ক সলনি কৰি চেষ্টা কৰি চাওক। সলনি কৰিবলৈ টিপক।"</string>
<string name="EmergencyCallWarningTitle" msgid="813380189532491336">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
<string name="EmergencyCallWarningSummary" msgid="1899692069750260619">"ৱাই-ফাইৰ জৰিয়তে জৰুৰীকালীন কল কৰিব নোৱাৰি"</string>
<string name="notification_channel_network_alert" msgid="4427736684338074967">"সতৰ্কবাণীসমূহ"</string>
<string name="notification_channel_call_forward" msgid="2419697808481833249">"কল ফৰৱাৰ্ডিং"</string>
<string name="notification_channel_emergency_callback" msgid="6686166232265733921">"জৰুৰীকালীন ক\'লবেক ম\'ড"</string>
- <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"ম\'বাইল ডেটাৰ স্থিতি"</string>
+ <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"ম’বাইল ডেটাৰ স্থিতি"</string>
<string name="notification_channel_sms" msgid="3441746047346135073">"এছএমএছ বার্তাবোৰ"</string>
<string name="notification_channel_voice_mail" msgid="3954099424160511919">"ভইচমেইলৰ বাৰ্তাসমূহ"</string>
<string name="notification_channel_wfc" msgid="2130802501654254801">"ৱাই-ফাই কলিং"</string>
@@ -149,7 +149,7 @@
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ছেকেণ্ডৰ পাছত"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string>
- <string name="fcComplete" msgid="3118848230966886575">"সুবিধাৰ ক\'ড সম্পূর্ণ হ\'ল।"</string>
+ <string name="fcComplete" msgid="3118848230966886575">"সুবিধাৰ ক\'ড সম্পূর্ণ হ’ল।"</string>
<string name="fcError" msgid="3327560126588500777">"সংযোগত সমস্যা হৈছে বা সুবিধাৰ ক\'ড অমান্য।"</string>
<string name="httpErrorOk" msgid="1191919378083472204">"ঠিক আছে"</string>
<string name="httpError" msgid="7956392511146698522">"ইণ্টাৰনেট সম্পর্কীয় আসোঁৱাহ হ\'ল৷"</string>
@@ -176,13 +176,13 @@
<string name="low_memory" product="tv" msgid="516619861191025923">"টিভিৰ সঞ্চয়াগাৰ ভৰি পৰিছে। খালী ঠাই উলিয়াবলৈ কিছুমান ফাইল মচক।"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"ফ\'নৰ সঞ্চয়াগাৰত খালী ঠাই নাই। ঠাই খালী কৰিবলৈ কিছুমান ফাইল মচক।"</string>
<plurals name="ssl_ca_cert_warning" formatted="false" msgid="5106721205300213569">
- <item quantity="one">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ\'ল</item>
- <item quantity="other">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ\'ল</item>
+ <item quantity="one">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ’ল</item>
+ <item quantity="other">প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ’ল</item>
</plurals>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"এটা অজ্ঞাত তৃতীয় পক্ষৰদ্বাৰা"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="3541729986326153557">"আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ প্ৰশাসকে পৰ্যবেক্ষণ কৰি আছে"</string>
<string name="ssl_ca_cert_noti_managed" msgid="4030263497686867141">"<xliff:g id="MANAGING_DOMAIN">%s</xliff:g>ৰ দ্বাৰা"</string>
- <string name="work_profile_deleted" msgid="5005572078641980632">"কৰ্মস্থানৰ প্ৰ\'ফাইল মচা হ\'ল"</string>
+ <string name="work_profile_deleted" msgid="5005572078641980632">"কৰ্মস্থানৰ প্ৰ\'ফাইল মচা হ’ল"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ প্ৰশাসক এপ্ নাই বা ব্যৱহাৰযোগ্য হৈ থকা নাই। যাৰ ফলত আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল আৰু ইয়াৰ লগত জড়িত অন্য ডেটাসমূহ মচা হৈছে। সহায়ৰ বাবে আপোনাৰ প্ৰশাসকৰ সৈতে সম্পর্ক কৰক।"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল এই ডিভাইচটোত আৰু উপলব্ধ নহয়"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"বহুতবাৰ ভুলকৈ পাছৱৰ্ড দিয়া হৈছে"</string>
@@ -203,9 +203,9 @@
<string name="silent_mode_silent" msgid="319298163018473078">"ৰিংগাৰ অফ আছে"</string>
<string name="silent_mode_vibrate" msgid="7072043388581551395">"ৰিংগাৰ কম্পন অৱস্থাত আছে"</string>
<string name="silent_mode_ring" msgid="8592241816194074353">"ৰিংগাৰ অন আছে"</string>
- <string name="reboot_to_update_title" msgid="6212636802536823850">"Android ছিষ্টেমৰ আপডেট"</string>
- <string name="reboot_to_update_prepare" msgid="6305853831955310890">"আপডেট সাজু কৰি থকা হৈছে…"</string>
- <string name="reboot_to_update_package" msgid="3871302324500927291">"পেকেজ আপডেট কৰা প্ৰক্ৰিয়া চলি আছে…"</string>
+ <string name="reboot_to_update_title" msgid="6212636802536823850">"Android ছিষ্টেমৰ আপডে’ট"</string>
+ <string name="reboot_to_update_prepare" msgid="6305853831955310890">"আপডে’ট সাজু কৰি থকা হৈছে…"</string>
+ <string name="reboot_to_update_package" msgid="3871302324500927291">"পেকেজ আপডে’ট কৰা প্ৰক্ৰিয়া চলি আছে…"</string>
<string name="reboot_to_update_reboot" msgid="6428441000951565185">"ৰিষ্টাৰ্ট কৰা হৈছে…"</string>
<string name="reboot_to_reset_title" msgid="4142355915340627490">"ফেক্টৰী ডেটা ৰিছেট কৰক"</string>
<string name="reboot_to_reset_message" msgid="2432077491101416345">"ৰিষ্টার্ট হৈ আছে…"</string>
@@ -314,20 +314,20 @@
<string name="permgrouplab_aural" msgid="965607064083134896">"সংগীত"</string>
<string name="permgroupdesc_aural" msgid="4870189506255958055">"আপোনাৰ সংগীত এক্সেছ কৰিবলৈ"</string>
<string name="permgrouprequest_aural" msgid="6787926123071735620">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক আপোনাৰ সংগীত এক্সেছ কৰিবলৈ দিবনে?"</string>
- <string name="permgrouplab_visual" msgid="8030190588123857921">"ফট\' আৰু ভিডিঅ’সমূহ"</string>
+ <string name="permgrouplab_visual" msgid="8030190588123857921">"ফট’ আৰু ভিডিঅ’"</string>
<string name="permgroupdesc_visual" msgid="3415827902566663546">"আপোনাৰ ফট’ আৰু ভিডিঅ’সমূহ এক্সেছ কৰিবলৈ"</string>
<string name="permgrouprequest_visual" msgid="6907523945030290376">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক আপোনাৰ ফট’ আৰু ভিডিঅ’সমূহ এক্সেছ কৰিবলৈ দিবনে?"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ৱিণ্ড\' সমল বিচাৰি উলিয়াওক"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"আপুনি যোগাযোগ কৰি থকা ৱিণ্ড\'খনৰ সমল পৰীক্ষা কৰক।"</string>
- <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰক"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"টেপ কৰা বস্তুসমূহ ডাঙৰকৈ কোৱা হ\'ব আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ দি স্ক্ৰীণ অন্বেষণ কৰিব পাৰিব।"</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপুনি লিখা পাঠ নিৰীক্ষণ কৰক"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্ৰেডিট কাৰ্ডৰ নম্বৰ আৰু পাছৱৰ্ডৰ দৰে ব্যক্তিগত ডেটা অন্তৰ্ভুক্ত হ\'ব পাৰে।"</string>
- <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ডিছপ্লেৰ বিবৰ্ধন নিয়ন্ত্ৰণ কৰক"</string>
- <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ডিছপ্লেৰ জুমৰ স্তৰ আৰু অৱস্থান নিয়ন্ত্ৰণ কৰক।"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ৱিণ্ড’ সমল বিচাৰি উলিওৱাৰ"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"আপুনি চাই থকা ৱিণ্ড’খনৰ সমল পৰীক্ষা কৰাৰ।"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰাৰ"</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"আঙুলিৰে টিপা বস্তুসমূহ ডাঙৰকৈ কৈ শুনোৱা হ’ব আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ দি স্ক্ৰীণ অন্বেষণ কৰিব পাৰিব।"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপুনি লিখা পাঠ নিৰীক্ষণ কৰাৰ"</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্ৰেডিট কাৰ্ডৰ নম্বৰ আৰু পাছৱৰ্ডৰ দৰে ব্যক্তিগত ডেটা ইয়াত অন্তৰ্ভুক্ত।"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ডিছপ্লে’ৰ বিবৰ্ধন নিয়ন্ত্ৰণ কৰাৰ"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ডিছপ্লে’ৰ জুমৰ স্তৰ আৰু অৱস্থান নিয়ন্ত্ৰণ কৰাৰ।"</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ কৰা কার্যসমূহ কৰক"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"টেপ কৰা, ছোৱাইপ কৰা, পিঞ্চ কৰা আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ কৰা অন্যান্য কাৰ্যসমূহ কৰিব পাৰে।"</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ফিংগাৰপ্ৰিণ্ট নিৰ্দেশসমূহ"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ফিংগাৰপ্ৰিণ্ট নিৰ্দেশ"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ডিভাইচটোৰ ফিংগাৰপ্ৰিণ্ট ছেন্সৰত দিয়া নিৰ্দেশ বুজিব পাৰে।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"স্থিতি দণ্ড অক্ষম কৰক বা সলনি কৰক"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"স্থিতি দণ্ড অক্ষম কৰিবলৈ বা ছিষ্টেম আইকন আঁতৰাবলৈ এপটোক অনুমতি দিয়ে।"</string>
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ডিভাইচত থকা ফ\'নৰ সুবিধাসমূহ ব্য়ৱহাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে৷ এই অনুমতিয়ে কোনো কল সক্ৰিয় হৈ থাককেই বা নাথাকক আৰু দূৰবৰ্তী নম্বৰটো কলৰ দ্বাৰা সংযোজিত হওকেই বা নহওক এপটোক ফ\'ন নম্বৰ আৰু ডিভাইচৰ পৰিচয় নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে৷"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ছিষ্টেমৰ জৰিয়তে কল কৰিব পাৰে"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"কল কৰাৰ অভিজ্ঞতাক উন্নত কৰিবলৈ এপটোক ছিষ্টেমৰ জৰিয়তে কলসমূহ কৰিবলৈ দিয়ে।"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ছিষ্টেমৰ জৰিয়তে কলবোৰ চোৱা আৰু নিয়ন্ত্ৰণ কৰা।"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"এপটোক ডিভাইচত চলি থকা কল চাবলৈ আৰু নিয়ন্ত্ৰণ কৰিবলৈ অনুমতি দিয়ে। কলৰ সংখ্যা আৰু কলবোৰৰ স্থিতি ইয়াত অন্তৰ্ভুক্ত হয়।"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"অইন এটা এপত আৰম্ভ হোৱা কল এটা অব্যাহত ৰাখিব পাৰে"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"এপটোক এনে কল কৰিবলৈ দিয়ে যিটোৰ আৰম্ভণি অইন এটা এপত হৈছিল।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ফ\'ন নম্বৰসমূহ পঢ়ে"</string>
@@ -482,7 +484,7 @@
<string name="permlab_changeTetherState" msgid="5952584964373017960">"টেডাৰিং সংযোগ সলনি কৰক"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"টেডাৰ হৈ থকা ইণ্টাৰনেট সংযোগৰ অৱস্থা সলনি কৰিবলৈ এপটোক অনুমতি দিয়ে৷"</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"ৱাই-ফাইৰ সংযোগবোৰ চাওক"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"ৱাই-ফাই সক্ষম কৰা হ\'ল নে নাই আৰু সংযোগ হৈ থকা ৱাই-ফাই ডিভাইচসমূহৰ নামবোৰৰ দৰে ৱাই-ফাইৰ ইণ্টাৰনেট সম্পর্কীয় তথ্য চাবলৈ এপক অনুমতি দিয়ে।"</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"ৱাই-ফাই সক্ষম কৰা হ’ল নে নাই আৰু সংযোগ হৈ থকা ৱাই-ফাই ডিভাইচসমূহৰ নামবোৰৰ দৰে ৱাই-ফাইৰ ইণ্টাৰনেট সম্পর্কীয় তথ্য চাবলৈ এপক অনুমতি দিয়ে।"</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"ৱাই-ফাই সংযোগ কৰক আৰু ইয়াৰ সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"এপটোক ৱাই-ফাই এক্সেছ পইণ্টলৈ সংযোগ কৰিবলৈ আৰু তাৰ সংযোগ বিচ্ছিন্ন কৰিবলৈ আৰু ৱাই-ফাই নেটৱৰ্কসমূহৰ বাবে ডিভাইচ কনফিগাৰেশ্বনত সাল-সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"ৱাই-ফাই মাল্টিকাষ্ট প্ৰচাৰৰ অনুমতি দিয়ক"</string>
@@ -506,7 +508,9 @@
<string name="permlab_nfc" msgid="4423351274757876953">"নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"এপটোক নিয়েৰ ফিল্ড কমিউনিকেশ্বন (NFC) টেগ, কাৰ্ড আৰু ৰিডাৰসমূহৰ সৈতে যোগাযোগ কৰিবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"আপোনাৰ স্ক্ৰীণ ল\'ক অক্ষম কৰক"</string>
- <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণ স্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰপিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷"</string>
+ <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণস্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰ পিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"স্ক্ৰীণ ল’কৰ জটিলতা লাভ কৰক আৰু অনুৰোধ কৰক"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"স্ক্ৰীণ ল’কৰ জটিলতাৰ স্তৰ (উচ্চ, মধ্যম, নিম্ন বা একেবাৰে নাই), যি স্ক্ৰীণ ল’কৰ সম্ভাব্য দৈৰ্ঘ্য বা স্ক্ৰীণ ল’কৰ প্ৰকাৰ দৰ্শায় সেইবোৰ শিকিবলৈ এপক অনুমতি দিয়ে। লগতে এপটোৱে ব্যৱহাৰকাৰীক স্ক্ৰীণ ল’কটো এটা নিৰ্দিষ্ট স্তৰলৈ আপডে’ট কৰিবলৈ পৰামৰ্শ দিব পাৰে যিটো ব্যৱহাৰকাৰীয়ে উপেক্ষা কৰি পৰৱর্তী পৃষ্ঠালৈ যাব পাৰে। মনত ৰাখিব যে স্ক্ৰীণ ল’কটো সাধাৰণ পাঠ হিচাপে সঞ্চয় কৰা নহয় সেয়ে এপটোৱে সঠিক পাছৱৰ্ডটো জানিব নোৱাৰে।"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰক"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ পৰিচালনা কৰিব পাৰে"</string>
@@ -542,15 +546,15 @@
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
- <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ\'ল"</string>
- <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ\'ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
+ <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string>
+ <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ফিংগাৰপ্ৰিণ্ট সঞ্চয় কৰিব পৰা নগ\'ল। পূর্বে সঞ্চিত ফিংগাৰপ্ৰিণ্ট এটা আঁতৰাওক।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ফিংগাৰপ্ৰিণ্ট গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।"</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ\'ল।"</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ’ল।"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্ট ক্ৰিয়া বাতিল কৰিছে।"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"অত্যধিক প্ৰয়াস। ফিংগাৰপ্ৰিণ্ট ছেন্সৰ অক্ষম কৰা হ\'ল।"</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"অত্যধিক প্ৰয়াস। ফিংগাৰপ্ৰিণ্ট ছেন্সৰ অক্ষম কৰা হ’ল।"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"আকৌ চেষ্টা কৰক।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
<string name="fingerprint_error_hw_not_present" msgid="5729436878065119329">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই"</string>
@@ -593,7 +597,7 @@
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ছিংকৰ ছেটিংসমূহ পঢ়ক"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ কোনো একাউণ্টত ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ছিংকক অন আৰু অফ ট\'গল কৰক"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণ স্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷"</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণস্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"ছিংকৰ পৰিসংখ্যা পঢ়ক"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"ছিংকৰ কাৰ্যক্ৰমসমূহৰ ইতিহাস আৰু ছিংক কৰা ডেটাৰ পৰিমাণসহ কোনো একাউণ্টৰ ছিংকৰ তথ্য পঢ়িবলৈ এপক অনুমতি দিয়ে।"</string>
<string name="permlab_sdcardRead" msgid="1438933556581438863">"আপোনাৰ শ্বেয়াৰ কৰি ৰখা সঞ্চয়াগাৰৰ সমল পঢ়িব পাৰে"</string>
@@ -679,7 +683,7 @@
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"স্ক্ৰীণ লকৰ কিছুমান সুবিধা ব্যৱহাৰ হোৱাত বাধা দিয়ক।"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"ঘৰ"</item>
- <item msgid="869923650527136615">"ম\'বাইল"</item>
+ <item msgid="869923650527136615">"ম’বাইল"</item>
<item msgid="7897544654242874543">"কৰ্মস্থান"</item>
<item msgid="1103601433382158155">"কর্মস্থানৰ ফেক্সৰ নম্বৰ"</item>
<item msgid="1735177144948329370">"ঘৰৰ ফেক্স নম্বৰ"</item>
@@ -722,7 +726,7 @@
</string-array>
<string name="phoneTypeCustom" msgid="1644738059053355820">"নিজৰ উপযোগিতা অনুযায়ী"</string>
<string name="phoneTypeHome" msgid="2570923463033985887">"ঘৰৰ ফ\'ন নম্বৰ"</string>
- <string name="phoneTypeMobile" msgid="6501463557754751037">"ম\'বাইল"</string>
+ <string name="phoneTypeMobile" msgid="6501463557754751037">"ম’বাইল"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"কৰ্মস্থানৰ ফ\'ন নম্বৰ"</string>
<string name="phoneTypeFaxWork" msgid="3517792160008890912">"কর্মস্থানৰ ফেক্সৰ নম্বৰ"</string>
<string name="phoneTypeFaxHome" msgid="2067265972322971467">"ঘৰৰ ফেক্স নম্বৰ"</string>
@@ -737,7 +741,7 @@
<string name="phoneTypeRadio" msgid="4093738079908667513">"ৰেডিঅ’ ফ\'ন নম্বৰ"</string>
<string name="phoneTypeTelex" msgid="3367879952476250512">"টেলেক্স ফ\'ন নম্বৰ"</string>
<string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
- <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"কৰ্মস্থানৰ ম\'বাইল নম্বৰ"</string>
+ <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"কৰ্মস্থানৰ ম’বাইল নম্বৰ"</string>
<string name="phoneTypeWorkPager" msgid="649938731231157056">"কৰ্মস্থানৰ পেজাৰৰ নম্বৰ"</string>
<string name="phoneTypeAssistant" msgid="5596772636128562884">"সহায়ক"</string>
<string name="phoneTypeMms" msgid="7254492275502768992">"এমএমএছ"</string>
@@ -749,7 +753,7 @@
<string name="emailTypeHome" msgid="449227236140433919">"ঘৰ"</string>
<string name="emailTypeWork" msgid="3548058059601149973">"কৰ্মস্থান"</string>
<string name="emailTypeOther" msgid="2923008695272639549">"অন্যান্য"</string>
- <string name="emailTypeMobile" msgid="119919005321166205">"ম\'বাইল"</string>
+ <string name="emailTypeMobile" msgid="119919005321166205">"ম’বাইল"</string>
<string name="postalTypeCustom" msgid="8903206903060479902">"নিজৰ উপযোগিতা অনুযায়ী"</string>
<string name="postalTypeHome" msgid="8165756977184483097">"ঘৰৰ ঠিকনা"</string>
<string name="postalTypeWork" msgid="5268172772387694495">"কৰ্মস্থানৰ ঠিকনা"</string>
@@ -802,7 +806,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"আনলক কৰিবলৈ মেনু টিপাৰ পিছত ০ টিপক।"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"জৰুৰীকালীন নম্বৰ"</string>
<string name="lockscreen_carrier_default" msgid="6169005837238288522">"কোনো সেৱা নাই"</string>
- <string name="lockscreen_screen_locked" msgid="7288443074806832904">"স্ক্ৰীণ লক কৰা হ\'ল।"</string>
+ <string name="lockscreen_screen_locked" msgid="7288443074806832904">"স্ক্ৰীণ লক কৰা হ’ল।"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"আনলক কৰিবলৈ বা জৰুৰীকালীন কল কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"আনলক কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"আনলক কৰিবলৈ আর্হি আঁকক"</string>
@@ -812,7 +816,7 @@
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"আকৌ চেষ্টা কৰক"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"আকৌ চেষ্টা কৰক"</string>
<string name="lockscreen_storage_locked" msgid="9167551160010625200">"সকলো সুবিধা আৰু ডেটাৰ বাবে আনলক কৰক"</string>
- <string name="faceunlock_multiple_failures" msgid="754137583022792429">"গৰাকীৰ মুখাৱয়বৰ দ্বাৰা আনলক কৰা সর্বধিক সীমা অতিক্ৰম কৰা হ\'ল"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"গৰাকীৰ মুখাৱয়বৰ দ্বাৰা আনলক কৰা সর্বধিক সীমা অতিক্ৰম কৰা হ’ল"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"কোনো ছিম কাৰ্ড নাই"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"টে\'বলেটত ছিম কার্ড নাই।"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"টিভিত ছিম কার্ড নাই।"</string>
@@ -849,7 +853,7 @@
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> ছেকেণ্ডৰ পাছত চেষ্টা কৰক।"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"আর্হি পাহৰিলে নেকি?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"একাউণ্ট আনলক"</string>
- <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"বহুতবাৰ ভুলকৈ আর্হি অঁকা হ\'ল"</string>
+ <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"বহুতবাৰ ভুলকৈ আর্হি অঁকা হ’ল"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"আনলক কৰিবলৈ নিজৰ Google একাউণ্টৰ জৰিয়তে ছাইন ইন কৰক।"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ব্যৱহাৰকাৰীৰ নাম (ইমেইল)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"পাছৱৰ্ড"</string>
@@ -860,11 +864,11 @@
<string name="lockscreen_unlock_label" msgid="737440483220667054">"আনলক"</string>
<string name="lockscreen_sound_on_label" msgid="9068877576513425970">"ধ্বনি অন হৈ আছে"</string>
<string name="lockscreen_sound_off_label" msgid="996822825154319026">"ধ্বনি অফ হৈ আছে"</string>
- <string name="lockscreen_access_pattern_start" msgid="3941045502933142847">"আর্হি অঁকা কার্য আৰম্ভ হ\'ল"</string>
- <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"আর্হি মচি পেলোৱা হ\'ল"</string>
+ <string name="lockscreen_access_pattern_start" msgid="3941045502933142847">"আর্হি অঁকা কার্য আৰম্ভ হ’ল"</string>
+ <string name="lockscreen_access_pattern_cleared" msgid="5583479721001639579">"আর্হি মচি পেলোৱা হ’ল"</string>
<string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"আৰ্হিত এটা বিন্দু যোগ কৰিছে"</string>
- <string name="lockscreen_access_pattern_cell_added_verbose" msgid="7264580781744026939">"চেল <xliff:g id="CELL_INDEX">%1$s</xliff:g> যোগ কৰা হ\'ল"</string>
- <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"আর্হি অঁকা সর্ম্পূণ হ\'ল"</string>
+ <string name="lockscreen_access_pattern_cell_added_verbose" msgid="7264580781744026939">"চেল <xliff:g id="CELL_INDEX">%1$s</xliff:g> যোগ কৰা হ’ল"</string>
+ <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"আর্হি অঁকা সর্ম্পূণ হ’ল"</string>
<string name="lockscreen_access_pattern_area" msgid="400813207572953209">"আর্হিৰ ক্ষেত্ৰ।"</string>
<string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. ৱিজেট %3$d-ৰ %2$d।"</string>
<string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ৱিজেট যোগ কৰক।"</string>
@@ -876,9 +880,9 @@
<string name="keyguard_accessibility_status" msgid="8008264603935930611">"স্থিতি"</string>
<string name="keyguard_accessibility_camera" msgid="8904231194181114603">"কেমেৰা"</string>
<string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ"</string>
- <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"ৱিজেটৰ পুনঃক্ৰম আৰম্ভ হ\'ল।"</string>
- <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"ৱিজেটৰ পুনঃক্ৰম সমাপ্ত হ\'ল।"</string>
- <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"ৱিজেট <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> মচা হ\'ল।"</string>
+ <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"ৱিজেটৰ পুনঃক্ৰম আৰম্ভ হ’ল।"</string>
+ <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"ৱিজেটৰ পুনঃক্ৰম সমাপ্ত হ’ল।"</string>
+ <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"ৱিজেট <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> মচা হ’ল।"</string>
<string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"আনলক ক্ষেত্ৰ বিস্তাৰ কৰক।"</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"শ্লাইডৰদ্বাৰা আনলক।"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"আৰ্হিৰদ্বাৰা আনলক।"</string>
@@ -934,7 +938,7 @@
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"আপোনাৰ টিভিত সঞ্চয় কৰি ৰখা ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰৰ ইতিহাস সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিংৰ ক্ষমতা থকা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।"</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"আপোনাৰ ফ\'নত সঞ্চয় কৰি ৰখা ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰৰ ইতিহাস সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিং কৰিব পৰা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।"</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"এলাৰ্ম ছেট কৰক"</string>
- <string name="permdesc_setAlarm" msgid="316392039157473848">"এপটোক ইনষ্টল হৈ থকা এলাৰ্ম ক্লক এপত এলাৰ্ম ছেট কৰিবলৈ অনুমতি দিয়ে। কিছুমান এলাৰ্ম ক্লক এপত এই সুবিধাটো প্ৰযোজ্য নহ\'ব পাৰে।"</string>
+ <string name="permdesc_setAlarm" msgid="316392039157473848">"এপটোক ইনষ্টল হৈ থকা এলাৰ্ম ক্লক এপত এলাৰ্ম ছেট কৰিবলৈ অনুমতি দিয়ে। কিছুমান এলাৰ্ম ক্লক এপত এই সুবিধাটো প্ৰযোজ্য নহ’ব পাৰে।"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ভইচমেইল যোগ কৰক"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"আপোনাৰ ভইচমেইল ইনবক্সত বাৰ্তাবোৰ যোগ কৰিবলৈ এপটোক অনুমতি দিয়ক।"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ব্ৰাউজাৰৰ জিঅ\'লোকেশ্বনৰ অনুমতিসমূহ সংশোধন কৰক"</string>
@@ -944,7 +948,7 @@
<string name="save_password_remember" msgid="6491879678996749466">"মনত ৰাখিব"</string>
<string name="save_password_never" msgid="8274330296785855105">"কেতিয়াও মনত নাৰাখিব"</string>
<string name="open_permission_deny" msgid="7374036708316629800">"এই পৃষ্ঠাটো খুলিবলৈ আপোনাৰ অনুমতি নাই।"</string>
- <string name="text_copied" msgid="4985729524670131385">"ক্লিপব\'র্ডলৈ বাৰ্তা প্ৰতিলিপি কৰা হ\'ল।"</string>
+ <string name="text_copied" msgid="4985729524670131385">"ক্লিপব\'র্ডলৈ বাৰ্তা প্ৰতিলিপি কৰা হ’ল।"</string>
<string name="more_item_label" msgid="4650918923083320495">"অধিক"</string>
<string name="prepend_shortcut_label" msgid="2572214461676015642">"মেনু+"</string>
<string name="menu_meta_shortcut_label" msgid="4647153495550313570">"মেটা+"</string>
@@ -1083,22 +1087,22 @@
<string name="deleteText" msgid="6979668428458199034">"মচক"</string>
<string name="inputMethod" msgid="1653630062304567879">"ইনপুট পদ্ধতি"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"পাঠ বিষয়ক কাৰ্য"</string>
- <string name="email" msgid="4560673117055050403">"ইমেইল"</string>
- <string name="email_desc" msgid="3638665569546416795">"বাছনি কৰা ঠিকনাত ইমেইল কৰক"</string>
+ <string name="email" msgid="4560673117055050403">"ইমেইল কৰক"</string>
+ <string name="email_desc" msgid="3638665569546416795">"বাছনি কৰা ঠিকনালৈ ইমেইল পঠিয়াওক"</string>
<string name="dial" msgid="1253998302767701559">"কল কৰক"</string>
<string name="dial_desc" msgid="6573723404985517250">"বাছনি কৰা ফ\'ন নাম্বাৰত কল কৰক"</string>
- <string name="map" msgid="5441053548030107189">"মেপ"</string>
+ <string name="map" msgid="5441053548030107189">"মেপ খোলক"</string>
<string name="map_desc" msgid="1836995341943772348">"বাছনি কৰা ঠিকনাটো বিচাৰি উলিয়াওক"</string>
<string name="browse" msgid="1245903488306147205">"খোলক"</string>
<string name="browse_desc" msgid="8220976549618935044">"বাছনি কৰা URL খোলক"</string>
- <string name="sms" msgid="4560537514610063430">"বাৰ্তা"</string>
- <string name="sms_desc" msgid="7526588350969638809">"বাছনি কৰা ফ\'ন নাম্বাৰত বাৰ্তা পঠিয়াওক"</string>
+ <string name="sms" msgid="4560537514610063430">"বাৰ্তা পঠিয়াওক"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"বাছনি কৰা ফ’ন নম্বৰলৈ বাৰ্তা পঠিয়াওক"</string>
<string name="add_contact" msgid="7867066569670597203">"যোগ দিয়ক"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"সর্ম্পকসূচীত যোগ কৰক"</string>
<string name="view_calendar" msgid="979609872939597838">"চাওক"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"কেলেণ্ডাৰত বাছনি কৰা সময় চাওক"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"সূচী"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"বাছনি কৰা সময়ৰ বাবে কাৰ্যক্ৰমৰ তালিকা বনাওক"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"সময়সূচী নিৰ্ধাৰণ কৰক"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"বাছনি কৰা তাৰিখটো কাৰ্যক্ৰমৰ সময় হিচাপে নিৰ্ধাৰণ কৰক"</string>
<string name="view_flight" msgid="7691640491425680214">"ট্ৰেক কৰক"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"বাছনি কৰা বিমানটো ট্ৰেক কৰক"</string>
<string name="translate" msgid="9218619809342576858">"অনুবাদ কৰক"</string>
@@ -1141,12 +1145,12 @@
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"প্ৰতিচ্ছবি তোলক"</string>
<string name="alwaysUse" msgid="4583018368000610438">"এই কার্যৰ বাবে পূর্বনির্ধাৰিত ধৰণে ব্যৱহাৰ কৰক।"</string>
<string name="use_a_different_app" msgid="8134926230585710243">"এটা পৃথক এপ্ ব্যৱহাৰ কৰক"</string>
- <string name="clearDefaultHintMsg" msgid="3252584689512077257">"ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল\'ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
+ <string name="clearDefaultHintMsg" msgid="3252584689512077257">"ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল’ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
<string name="chooseActivity" msgid="7486876147751803333">"কোনো কার্য বাছনি কৰক"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"ইউএছবি ডিভাইচৰ বাবে এটা এপ্ বাছনি কৰক"</string>
<string name="noApplications" msgid="2991814273936504689">"কোনো এপে এই কাৰ্য কৰিব নোৱাৰে।"</string>
- <string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> বন্ধ হ\'ল"</string>
- <string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> বন্ধ হ\'ল"</string>
+ <string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> বন্ধ হ’ল"</string>
+ <string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> বন্ধ হ’ল"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> বাৰে বাৰে বন্ধ হৈ গৈছে"</string>
<string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> বাৰে বাৰে বন্ধ হৈ গৈছে"</string>
<string name="aerr_restart" msgid="7581308074153624475">"আকৌ এপটো খোলক"</string>
@@ -1169,22 +1173,23 @@
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক পূৰ্বতে লঞ্চ কৰা হৈছিল৷"</string>
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"স্কেল"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"সদায় দেখুৱাওক"</string>
- <string name="screen_compat_mode_hint" msgid="1064524084543304459">"ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল\'ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
- <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বর্তমানৰ ডিছপ্লেৰ আকাৰ ছেটিং ব্যৱহাৰ কৰিব নোৱাৰে আৰু ই সঠিকভাৱে নচলিবও পাৰে।"</string>
+ <string name="screen_compat_mode_hint" msgid="1064524084543304459">"ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল’ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
+ <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বর্তমানৰ ডিছপ্লে’ৰ আকাৰ ছেটিং ব্যৱহাৰ কৰিব নোৱাৰে আৰু ই সঠিকভাৱে নচলিবও পাৰে।"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"সদায় দেখুৱাওক"</string>
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক এটা খাপ নোখোৱা Android OS সংস্কৰণৰ বাবে তৈয়াৰ কৰা হৈছিল, যাৰ ফলত ই অস্বাভাৱিকধৰণে আচৰণ কৰিব পাৰে। এপটোৰ শেহতীয়া সংস্কৰণ উপলব্ধ হ\'ব পাৰে।"</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"সদায় দেখুৱাওক"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"আপডেট আছে নেকি চাওক"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"আপডে’ট আছে নেকি চাওক"</string>
<string name="smv_application" msgid="3307209192155442829">"এপটোৱে <xliff:g id="APPLICATION">%1$s</xliff:g> (প্ৰক্ৰিয়াটোৱে <xliff:g id="PROCESS">%2$s</xliff:g>) নিজে বলবৎ কৰা StrictMode নীতি ভংগ কৰিলে।"</string>
<string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> প্ৰক্ৰিয়াটোৱে নিজে বলৱৎ কৰা StrictMode নীতি ভংগ কৰিলে।"</string>
- <string name="android_upgrading_title" product="default" msgid="7513829952443484438">"ফ\'নটো আপডেট হৈ আছে…"</string>
- <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"টেবলেটটো আপডেট হৈ আছে…"</string>
- <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ডিভাইচটো আপডেট হৈ আছে…"</string>
+ <string name="android_upgrading_title" product="default" msgid="7513829952443484438">"ফ\'নটো আপডে’ট হৈ আছে…"</string>
+ <string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"টেবলেটটো আপডে’ট হৈ আছে…"</string>
+ <string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ডিভাইচটো আপডে’ট হৈ আছে…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ফ\'নটো আৰম্ভ হৈছে…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android আৰম্ভ কৰি থকা হৈছে…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"টেবলেটটো আৰম্ভ হৈছে…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ডিভাইচটো আৰম্ভ হৈছে…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"সঞ্চয়াগাৰ অপ্টিমাইজ কৰি থকা হৈছে।"</string>
- <string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"ছিষ্টেম আপডেট সম্পূৰ্ণ কৰা হৈছে…"</string>
+ <string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"ছিষ্টেম আপডে’ট সম্পূৰ্ণ কৰা হৈছে…"</string>
<string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g>ক আপগ্ৰেড কৰি থকা হৈছে…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>ৰ ভিতৰত <xliff:g id="NUMBER_0">%1$d</xliff:g> এপ্ অপ্টিমাইজ কৰি থকা হৈছে৷"</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>সাজু কৰি থকা হৈছে।"</string>
@@ -1198,7 +1203,7 @@
<string name="new_app_action" msgid="6694851182870774403">"<xliff:g id="NEW_APP">%1$s</xliff:g> খোলক"</string>
<string name="new_app_description" msgid="5894852887817332322">"<xliff:g id="OLD_APP">%1$s</xliff:g> ছেভ নকৰাকৈ বন্ধ হ\'ব"</string>
<string name="dump_heap_notification" msgid="2618183274836056542">"<xliff:g id="PROC">%1$s</xliff:g> মেম\'ৰিৰ সীমা অতিক্ৰম কৰিছে"</string>
- <string name="dump_heap_notification_detail" msgid="3993078784053054141">"হীপ ডাম্প সংগ্ৰহ কৰা হ\'ল। শ্বেয়াৰ কৰিবলৈ টিপক"</string>
+ <string name="dump_heap_notification_detail" msgid="3993078784053054141">"হীপ ডাম্প সংগ্ৰহ কৰা হ’ল। শ্বেয়াৰ কৰিবলৈ টিপক"</string>
<string name="dump_heap_title" msgid="5864292264307651673">"হীপ ডাম্প শ্বেয়াৰ কৰিবনে?"</string>
<string name="dump_heap_text" msgid="4809417337240334941">"এই <xliff:g id="PROC">%1$s</xliff:g> প্ৰক্ৰিয়াটোৱে তাৰ মেম\'ৰিৰ সীমা <xliff:g id="SIZE">%2$s</xliff:g> অতিক্ৰম কৰিছে। ইয়াৰ বিকাশকৰ্তাৰ সৈতে আপুনি শ্বেয়াৰ কৰিবপৰাকৈ হীপ ডাম্প মজুত আছে। সাৱধান হ\'ব: এই হীপ ডাম্পত এপ্লিকেশ্বনটোৱে ব্যৱহাৰ কৰা আপোনাৰ কোনো ব্য়ক্তিগত তথ্য়ও থাকিব পাৰে।"</string>
<string name="sendText" msgid="5209874571959469142">"বার্তাৰ বাবে কাৰ্য বাছনি কৰক"</string>
@@ -1234,15 +1239,19 @@
<string name="wifi_available_title" msgid="3817100557900599505">"পাছৱৰ্ড অবিহনে সংযোগ কৰিবপৰা ৱাই-ফাই নেটৱর্কৰ সৈতে সংযোগ কৰক"</string>
<string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"বাহকৰ ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰক"</string>
<string name="wifi_available_title_connecting" msgid="1139126673968899002">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰি থকা হৈছে"</string>
- <string name="wifi_available_title_connected" msgid="7542672851522241548">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰা হ\'ল"</string>
+ <string name="wifi_available_title_connected" msgid="7542672851522241548">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰা হ’ল"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰিবপৰা নগ\'ল"</string>
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"সকলো নেটৱৰ্ক চাবলৈ টিপক"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"সংযোগ কৰক"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"সকলো নেটৱৰ্ক"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>এ প্ৰস্তাৱ দিয়া ৱাই-ফাই নেটৱৰ্কৰ লগত সংযোগ কৰা হ’ল"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"আপুনি <xliff:g id="NAME">%s</xliff:g>ক আপোনাৰ বাবে নেটৱৰ্কৰ প্ৰস্তাৱ দিয়াৰ অনুমতি দিব বিচাৰেনে?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"হয়"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"নহয়"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন হ\'ব"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"যেতিয়া আপুনি ছেভ কৰি থোৱা উচ্চ মানৰ নেটৱৰ্কৰ কাষত থাকে"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"পুনৰাই অন নকৰিব"</string>
- <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন কৰা হ\'ল"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন কৰা হ’ল"</string>
<string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"আপুনি ছেভ কৰি থোৱা নেটৱৰ্ক এটাৰ কাষত আছে: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
@@ -1253,11 +1262,11 @@
<string name="wifi_softap_config_change" msgid="8475911871165857607">"আপোনাৰ হটস্পট ছেটিংসমূহত কৰা সালসলনি"</string>
<string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"আপোনাৰ হটস্পটৰ বেণ্ড সলনি কৰা হ’ল।"</string>
<string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"আপোনাৰ কেৱল ৫গিগাহাৰ্টজৰ প্ৰতি অগ্ৰাধিকাৰ এই ডিভাচইচটোৱে সমৰ্থন নকৰে। ইয়াৰ পৰিৱৰ্তে, ডিভাচইচটোৱে যেতিয়া ৫গিগাহাৰ্টজ বেণ্ড উপলব্ধ হ’ব তেতিয়া সেইয়া ব্যৱহাৰ কৰিব।"</string>
- <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ\'ল"</string>
+ <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
- <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ\'ল"</string>
+ <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
<string-array name="network_switch_type_name">
- <item msgid="3979506840912951943">"ম\'বাইল ডেটা"</item>
+ <item msgid="3979506840912951943">"ম’বাইল ডেটা"</item>
<item msgid="75483255295529161">"ৱাই-ফাই"</item>
<item msgid="6862614801537202646">"ব্লুটুথ"</item>
<item msgid="5447331121797802871">"ইথাৰনেট"</item>
@@ -1276,7 +1285,7 @@
<string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"ছেটিংসমূহৰ বাবে টিপক"</string>
<string name="accept" msgid="1645267259272829559">"স্বীকাৰ কৰক"</string>
<string name="decline" msgid="2112225451706137894">"প্ৰত্যাখ্যান কৰক"</string>
- <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"আমন্ত্ৰণ পঠোৱা হ\'ল"</string>
+ <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"আমন্ত্ৰণ পঠোৱা হ’ল"</string>
<string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"সংযোগ হ\'বলৈ আমন্ত্ৰণ"</string>
<string name="wifi_p2p_from_message" msgid="570389174731951769">"প্ৰেৰক:"</string>
<string name="wifi_p2p_to_message" msgid="248968974522044099">"প্ৰতি:"</string>
@@ -1291,30 +1300,30 @@
<string name="sms_control_yes" msgid="3663725993855816807">"অনুমতি দিয়ক"</string>
<string name="sms_control_no" msgid="625438561395534982">"অস্বীকাৰ কৰক"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>এ <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> লৈ কিবা বার্তা প্ৰেৰণ কৰিব বিচাৰিছে।"</string>
- <string name="sms_short_code_details" msgid="5873295990846059400">"ইয়াৰ বাবে "<b>" মাছুল ভৰিবলগীয়া হ\'ব পাৰে"</b>"।"</string>
- <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"ইয়াৰ বাবে মাছুল ভৰিবলগীয়া হ\'ব পাৰে।"</b></string>
+ <string name="sms_short_code_details" msgid="5873295990846059400">"ইয়াৰ বাবে "<b>" মাচুল ভৰিবলগীয়া হ’ব পাৰে"</b>"।"</string>
+ <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"ইয়াৰ বাবে মাচুল ভৰিবলগীয়া হ’ব পাৰে।"</b></string>
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"পঠিয়াওক"</string>
<string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"বাতিল কৰক"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"মোৰ পচন্দ মনত ৰাখিব"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"আপুনি ইয়াক পিছত ছেটিং > এপ্-ত সলনি কৰিব পাৰে"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"যিকোনো সময়ত অনুমতি দিয়ক"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"কেতিয়াও অনুমতি নিদিব"</string>
- <string name="sim_removed_title" msgid="6227712319223226185">"ছিম কাৰ্ড আঁতৰোৱা হ\'ল"</string>
+ <string name="sim_removed_title" msgid="6227712319223226185">"ছিম কাৰ্ড আঁতৰোৱা হ’ল"</string>
<string name="sim_removed_message" msgid="2333164559970958645">"এখন মান্য ছিম কার্ড ব্যৱহাৰ কৰি ৰিষ্টার্ট নকৰা পর্যন্ত ম\'বাইলৰ নেটৱর্ক উপলব্ধ নহয়।"</string>
- <string name="sim_done_button" msgid="827949989369963775">"সম্পন্ন হ\'ল"</string>
- <string name="sim_added_title" msgid="3719670512889674693">"ছিম কাৰ্ড যোগ কৰা হ\'ল"</string>
+ <string name="sim_done_button" msgid="827949989369963775">"সম্পন্ন হ’ল"</string>
+ <string name="sim_added_title" msgid="3719670512889674693">"ছিম কাৰ্ড যোগ কৰা হ’ল"</string>
<string name="sim_added_message" msgid="6599945301141050216">"ম\'বাইলৰ নেটৱর্ক ব্যৱহাৰ কৰিবলৈ আপোনাৰ ডিভাইচটো ৰিষ্টার্ট কৰক।"</string>
<string name="sim_restart_button" msgid="4722407842815232347">"ৰিষ্টাৰ্ট কৰক"</string>
- <string name="install_carrier_app_notification_title" msgid="9056007111024059888">"ম\'বাইল সেৱা সক্ৰিয় কৰক"</string>
- <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ বাহকৰ এপটো ডাউনল\'ড কৰক"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g> এপটো ডাউনল\'ড কৰক"</string>
- <string name="install_carrier_app_notification_button" msgid="3094206295081900849">"এপ্ ডাউনল\'ড কৰক"</string>
+ <string name="install_carrier_app_notification_title" msgid="9056007111024059888">"ম’বাইল সেৱা সক্ৰিয় কৰক"</string>
+ <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ বাহকৰ এপটো ডাউনল’ড কৰক"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"আপোনাৰ নতুন ছিমখন সক্ৰিয় কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g> এপটো ডাউনল’ড কৰক"</string>
+ <string name="install_carrier_app_notification_button" msgid="3094206295081900849">"এপ্ ডাউনল’ড কৰক"</string>
<string name="carrier_app_notification_title" msgid="8921767385872554621">"নতুন ছিম ভৰোৱা হৈছে"</string>
<string name="carrier_app_notification_text" msgid="1132487343346050225">"ছেট আপ কৰিবলৈ টিপক"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"সময় ছেট কৰক"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"তাৰিখ ছেট কৰক"</string>
<string name="date_time_set" msgid="5777075614321087758">"ছেট কৰক"</string>
- <string name="date_time_done" msgid="2507683751759308828">"সম্পন্ন হ\'ল"</string>
+ <string name="date_time_done" msgid="2507683751759308828">"সম্পন্ন হ’ল"</string>
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"নতুন: "</font></string>
<string name="perms_description_app" msgid="5139836143293299417">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ প্ৰদান কৰা"</string>
<string name="no_permissions" msgid="7283357728219338112">"কোনো অনুমতিৰ প্ৰয়োজন নাই"</string>
@@ -1322,16 +1331,16 @@
<string name="dlg_ok" msgid="7376953167039865701">"ঠিক আছে"</string>
<string name="usb_charging_notification_title" msgid="1595122345358177163">"ইউএছবিৰ জৰিয়তে এই ডিভাইচটো চ্চাৰ্জ কৰি থকা হৈছে"</string>
<string name="usb_supplying_notification_title" msgid="4631045789893086181">"ইউএছবিৰ জৰিয়তে সংযুক্ত ডিভাইচটো চ্চাৰ্জ কৰি থকা হৈছে"</string>
- <string name="usb_mtp_notification_title" msgid="4238227258391151029">"ইউএছবি জৰিয়তে ফাইল স্থানান্তৰণ অন কৰা হ\'ল"</string>
- <string name="usb_ptp_notification_title" msgid="5425857879922006878">"ইউএছবিৰ জৰিয়তে পিটিপি অন কৰা হ\'ল"</string>
- <string name="usb_tether_notification_title" msgid="3716143122035802501">"ইউএছবি টেডাৰিং অন কৰা হ\'ল"</string>
- <string name="usb_midi_notification_title" msgid="5356040379749154805">"ইউএছবিৰ জৰিয়তে এমআইডিআই অন কৰা হ\'ল"</string>
- <string name="usb_accessory_notification_title" msgid="1785694450621427730">"ইউএছবি সহায়ক সামগ্ৰী সংযোগ কৰা হ\'ল"</string>
+ <string name="usb_mtp_notification_title" msgid="4238227258391151029">"ইউএছবি জৰিয়তে ফাইল স্থানান্তৰণ অন কৰা হ’ল"</string>
+ <string name="usb_ptp_notification_title" msgid="5425857879922006878">"ইউএছবিৰ জৰিয়তে পিটিপি অন কৰা হ’ল"</string>
+ <string name="usb_tether_notification_title" msgid="3716143122035802501">"ইউএছবি টেডাৰিং অন কৰা হ’ল"</string>
+ <string name="usb_midi_notification_title" msgid="5356040379749154805">"ইউএছবিৰ জৰিয়তে এমআইডিআই অন কৰা হ’ল"</string>
+ <string name="usb_accessory_notification_title" msgid="1785694450621427730">"ইউএছবি সহায়ক সামগ্ৰী সংযোগ কৰা হ’ল"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"অধিক বিকল্পৰ বাবে টিপক।"</string>
<string name="usb_power_notification_message" msgid="4647527153291917218">"সংযুক্ত ডিভাইচ চ্চাৰ্জ কৰি থকা হৈছে। অধিক বিকল্পৰ বাবে টিপক।"</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"এনাল\'গ অডিঅ\' সহায়ক সামগ্ৰী পোৱা গৈছে"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"সংলগ্ন কৰা ডিভাইচটোৱে এই ফ\'নটোৰ সৈতে কাম কৰিব নোৱাৰে। অধিক জানিবলৈ টিপক।"</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"ইউএছবি ডিবাগিং সংযোগ কৰা হ\'ল"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"ইউএছবি ডিবাগিং সংযোগ কৰা হ’ল"</string>
<string name="adb_active_notification_message" msgid="7463062450474107752">"ইউএছবি ডিবাগিং বন্ধ কৰিবলৈ টিপক"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ইউএছবি ডিবাগিং অক্ষম কৰিবলৈ বাছনি কৰক।"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"বাগ সম্পর্কীয় অভিযোগ গ্ৰহণ কৰি থকা হৈছে…"</string>
@@ -1364,9 +1373,9 @@
<string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"<xliff:g id="NAME">%s</xliff:g>ক ব্যৱহাৰ কৰিব নোৱাৰি"</string>
<string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"এই ডিভাইচটোৱে <xliff:g id="NAME">%s</xliff:g>ক ব্যৱহাৰ কৰিব নোৱাৰে। ব্যৱহাৰ কৰিব পৰা ফৰ্মেটত ছেট আপ কৰিবলৈ টিপক।"</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"এই ডিভাইচটোৱে <xliff:g id="NAME">%s</xliff:g>ক চলাব নোৱাৰে। চলাব পৰা কোনো ফৰ্মেটত ছেট আপ কৰিবলৈ বাছনি কৰক।"</string>
- <string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> অপ্ৰত্য়াশিতভাৱে আঁতৰোৱা হ\'ল"</string>
+ <string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> অপ্ৰত্য়াশিতভাৱে আঁতৰোৱা হ’ল"</string>
<string name="ext_media_badremoval_notification_message" msgid="8556885808951260574">"সমল হেৰুওৱাৰ পৰা হাত সাৰিবলৈ আঁতৰোৱাৰ আগতে মিডিয়া বাহিৰ কৰক"</string>
- <string name="ext_media_nomedia_notification_title" msgid="6593814191061956856">"<xliff:g id="NAME">%s</xliff:g> আঁতৰোৱা হ\'ল"</string>
+ <string name="ext_media_nomedia_notification_title" msgid="6593814191061956856">"<xliff:g id="NAME">%s</xliff:g> আঁতৰোৱা হ’ল"</string>
<string name="ext_media_nomedia_notification_message" msgid="2110883356419799994">"কিছুমান কাৰ্যক্ষমতাই সঠিকভাৱে কাম নকৰিব পাৰে। নতুন সঞ্চয়াগাৰ ভৰাওক।"</string>
<string name="ext_media_unmounting_notification_title" msgid="5046532339291216076">"<xliff:g id="NAME">%s</xliff:g> বাহিৰ কৰি থকা হৈছে"</string>
<string name="ext_media_unmounting_notification_message" msgid="1003926904442321115">"আঁতৰাই নিদিব"</string>
@@ -1377,16 +1386,16 @@
<string name="ext_media_missing_message" msgid="4012389235250987930">"ডিভাইচ আকৌ ভৰাওক"</string>
<string name="ext_media_move_specific_title" msgid="1471100343872375842">"<xliff:g id="NAME">%s</xliff:g>ক স্থানান্তৰ কৰি থকা হৈছে"</string>
<string name="ext_media_move_title" msgid="1022809140035962662">"ডেটা স্থানান্তৰ কৰি থকা হৈছে"</string>
- <string name="ext_media_move_success_title" msgid="7863652232242276066">"সমলৰ স্থানান্তৰণ সমাপ্ত হ\'ল"</string>
- <string name="ext_media_move_success_message" msgid="8939137931961728009">"<xliff:g id="NAME">%s</xliff:g>লৈ সমল স্থানান্তৰ কৰা হ\'ল"</string>
+ <string name="ext_media_move_success_title" msgid="7863652232242276066">"সমলৰ স্থানান্তৰণ সমাপ্ত হ’ল"</string>
+ <string name="ext_media_move_success_message" msgid="8939137931961728009">"<xliff:g id="NAME">%s</xliff:g>লৈ সমল স্থানান্তৰ কৰা হ’ল"</string>
<string name="ext_media_move_failure_title" msgid="1604422634177382092">"সমল স্থানান্তৰ কৰিব পৰা নগ\'ল"</string>
<string name="ext_media_move_failure_message" msgid="7388950499623016135">"সমল আকৌ স্থানান্তৰ কৰিবলৈ চেষ্টা কৰক"</string>
- <string name="ext_media_status_removed" msgid="6576172423185918739">"আঁতৰোৱা হ\'ল"</string>
- <string name="ext_media_status_unmounted" msgid="2551560878416417752">"বাহিৰলৈ উলিওৱা হ\'ল"</string>
+ <string name="ext_media_status_removed" msgid="6576172423185918739">"আঁতৰোৱা হ’ল"</string>
+ <string name="ext_media_status_unmounted" msgid="2551560878416417752">"বাহিৰলৈ উলিওৱা হ’ল"</string>
<string name="ext_media_status_checking" msgid="6193921557423194949">"পৰীক্ষা কৰি থকা হৈছে…"</string>
<string name="ext_media_status_mounted" msgid="7253821726503179202">"সাজু"</string>
<string name="ext_media_status_mounted_ro" msgid="8020978752406021015">"ৰীড-অনলি"</string>
- <string name="ext_media_status_bad_removal" msgid="8395398567890329422">"বিপজ্জনকভাৱে আঁতৰোৱা হ\'ল"</string>
+ <string name="ext_media_status_bad_removal" msgid="8395398567890329422">"বিপজ্জনকভাৱে আঁতৰোৱা হ’ল"</string>
<string name="ext_media_status_unmountable" msgid="805594039236667894">"ব্যৱহাৰযোগ্য নহয়"</string>
<string name="ext_media_status_unsupported" msgid="4691436711745681828">"সঞ্চয়াগাৰ ব্যৱহাৰ কৰিব নোৱাৰি"</string>
<string name="ext_media_status_ejecting" msgid="5463887263101234174">"বাহিৰলৈ উলিয়াই থকা হৈছে…"</string>
@@ -1409,7 +1418,7 @@
<string name="ime_action_search" msgid="658110271822807811">"অনুসন্ধান কৰক"</string>
<string name="ime_action_send" msgid="2316166556349314424">"পঠিয়াওক"</string>
<string name="ime_action_next" msgid="3138843904009813834">"পৰৱৰ্তী"</string>
- <string name="ime_action_done" msgid="8971516117910934605">"সম্পন্ন হ\'ল"</string>
+ <string name="ime_action_done" msgid="8971516117910934605">"সম্পন্ন হ’ল"</string>
<string name="ime_action_previous" msgid="1443550039250105948">"আগৰ"</string>
<string name="ime_action_default" msgid="2840921885558045721">"কার্য কৰক"</string>
<string name="dial_number_using" msgid="5789176425167573586">"<xliff:g id="NUMBER">%s</xliff:g> ব্যৱহাৰ কৰি \n নম্বৰটো ডায়েল কৰক"</string>
@@ -1437,7 +1446,7 @@
<string name="vpn_text" msgid="1610714069627824309">"নেটৱর্ক পৰিচালনা কৰিবলৈ টিপক।"</string>
<string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>ৰ সৈতে সংযোগ হৈছে। নেটৱর্ক পৰিচালনা কৰিবলৈ টিপক।"</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰি থকা হৈছে…"</string>
- <string name="vpn_lockdown_connected" msgid="8202679674819213931">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰা হ\'ল"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰা হ’ল"</string>
<string name="vpn_lockdown_disconnected" msgid="735805531187559719">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ বিচ্ছিন্ন কৰা হৈছে"</string>
<string name="vpn_lockdown_error" msgid="3133844445659711681">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ কৰিব পৰা নাই"</string>
<string name="vpn_lockdown_config" msgid="8151951501116759194">"নেটৱৰ্ক বা ভিপিএন ছেটিংসমূহ সলনি কৰক"</string>
@@ -1460,7 +1469,7 @@
<item quantity="one"><xliff:g id="TOTAL">%d</xliff:g>ৰ <xliff:g id="INDEX">%d</xliff:g>টা</item>
<item quantity="other"><xliff:g id="TOTAL">%d</xliff:g>ৰ <xliff:g id="INDEX">%d</xliff:g>টা</item>
</plurals>
- <string name="action_mode_done" msgid="7217581640461922289">"সম্পন্ন হ\'ল"</string>
+ <string name="action_mode_done" msgid="7217581640461922289">"সম্পন্ন হ’ল"</string>
<string name="progress_erasing" msgid="2569962663843586562">"শ্বেয়াৰ কৰি থোৱা সঞ্চয়াগাৰ মচি থকা হৈছে…"</string>
<string name="share" msgid="1778686618230011964">"শ্বেয়াৰ কৰক"</string>
<string name="find" msgid="4808270900322985960">"বিচাৰক"</string>
@@ -1472,7 +1481,7 @@
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)এ অনুৰোধ কৰিছে"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"হয়"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"নহয়"</string>
- <string name="sync_too_many_deletes" msgid="5296321850662746890">"মচি পেলোৱাৰ সীমা পাৰ হ\'ল"</string>
+ <string name="sync_too_many_deletes" msgid="5296321850662746890">"মচি পেলোৱাৰ সীমা পাৰ হ’ল"</string>
<string name="sync_too_many_deletes_desc" msgid="496551671008694245">"এই <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> ৰ মচি থোৱা <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> টা <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g> বস্তু আছে। আপুনি কি কৰিব বিচাৰে?"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"বস্তুবোৰ মচক"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"মচা কাৰ্যক আনডু কৰক"</string>
@@ -1501,7 +1510,7 @@
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"বাতিল কৰক"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"মচক"</string>
- <string name="keyboardview_keycode_done" msgid="1992571118466679775">"সম্পন্ন হ\'ল"</string>
+ <string name="keyboardview_keycode_done" msgid="1992571118466679775">"সম্পন্ন হ’ল"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ম\'ড সলনি"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"শ্বিফ্ট"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"এণ্টাৰ"</string>
@@ -1525,15 +1534,15 @@
<string name="extract_edit_menu_button" msgid="8940478730496610137">"সম্পাদনা কৰক"</string>
<string name="data_usage_warning_title" msgid="6499834033204801605">"ডেটা সকীয়নি"</string>
<string name="data_usage_warning_body" msgid="7340198905103751676">"আপুনি <xliff:g id="APP">%s</xliff:g> ডেটা ব্যৱহাৰ কৰিছে"</string>
- <string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"ম\'বাইল ডেটা ব্যৱাহৰৰ সীমা শেষ হৈছে"</string>
+ <string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"ম’বাইল ডেটা ব্যৱাহৰৰ সীমা শেষ হৈছে"</string>
<string name="data_usage_wifi_limit_title" msgid="5803363779034792676">"ৱাই-ফাই ডেটাৰ সীমাত উপনীত হৈছে"</string>
<string name="data_usage_limit_body" msgid="2908179506560812973">"আপোনাৰ ডেটা চক্ৰৰ বাকী অংশৰ বাবে ডেটা পজ কৰা হৈছে"</string>
- <string name="data_usage_mobile_limit_snoozed_title" msgid="3171402244827034372">"ম\'বাইল ডেটাৰ সীমা পাৰ কৰিছে"</string>
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="3171402244827034372">"ম’বাইল ডেটাৰ সীমা পাৰ কৰিছে"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="3547771791046344188">"ৱাই-ফাই ডেটাৰ সীমা পাৰ কৰিছে"</string>
<string name="data_usage_limit_snoozed_body" msgid="1671222777207603301">"আপুনি নিৰ্ধাৰিত সীমাতকৈ <xliff:g id="SIZE">%s</xliff:g> অধিক ব্যৱহাৰ কৰিছে"</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"নেপথ্য ডেটা সীমিত কৰি ৰখা হৈছে৷"</string>
<string name="data_usage_restricted_body" msgid="469866376337242726">"সীমাবদ্ধতা আঁতৰাবলৈ টিপক।"</string>
- <string name="data_usage_rapid_title" msgid="1809795402975261331">"অত্যধিক ম\'বাইল ডেটাৰ ব্যৱহাৰ"</string>
+ <string name="data_usage_rapid_title" msgid="1809795402975261331">"অত্যধিক ম’বাইল ডেটাৰ ব্যৱহাৰ"</string>
<string name="data_usage_rapid_body" msgid="6897825788682442715">"আপোনাৰ এপসমূহে সচৰাচৰতকৈ অধিক ডেটা ব্যৱহাৰ কৰিছে"</string>
<string name="data_usage_rapid_app_body" msgid="5396680996784142544">"<xliff:g id="APP">%s</xliff:g>এ সচৰাচৰতকৈ অধিক ডেটা ব্যৱহাৰ কৰিছে"</string>
<string name="ssl_certificate" msgid="6510040486049237639">"নিৰাপত্তা সম্পৰ্কীয় প্ৰমাণপত্ৰ"</string>
@@ -1568,7 +1577,7 @@
<string name="default_audio_route_name_usb" msgid="1234984851352637769">"ইউএছবি"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ছিষ্টেম"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ব্লুটুথ অডিঅ\'"</string>
- <string name="wireless_display_route_description" msgid="9070346425023979651">"ৱায়াৰলেচ ডিছপ্লে\'"</string>
+ <string name="wireless_display_route_description" msgid="9070346425023979651">"ৱায়াৰলেচ ডিছপ্লে’"</string>
<string name="media_route_button_content_description" msgid="591703006349356016">"কাষ্ট"</string>
<string name="media_route_chooser_title" msgid="1751618554539087622">"ডিভাইচৰ লগত সংযোগ কৰক"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"ডিভাইচত স্ক্ৰীণ কাষ্ট কৰক"</string>
@@ -1732,11 +1741,11 @@
<string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
<string name="mediasize_unknown_portrait" msgid="3088043641616409762">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string>
<string name="mediasize_unknown_landscape" msgid="4876995327029361552">"অজ্ঞাত লেণ্ডস্কেইপ"</string>
- <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"বাতিল কৰা হ\'ল"</string>
+ <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"বাতিল কৰা হ’ল"</string>
<string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"সমল লিখাত আসোঁৱাহ"</string>
<string name="reason_unknown" msgid="6048913880184628119">"অজ্ঞাত"</string>
<string name="reason_service_unavailable" msgid="7824008732243903268">"প্ৰিণ্টিং সেৱা সক্ষম নহয়"</string>
- <string name="print_service_installed_title" msgid="2246317169444081628">"<xliff:g id="NAME">%s</xliff:g> সেৱা ইনষ্টল কৰা হ\'ল"</string>
+ <string name="print_service_installed_title" msgid="2246317169444081628">"<xliff:g id="NAME">%s</xliff:g> সেৱা ইনষ্টল কৰা হ’ল"</string>
<string name="print_service_installed_message" msgid="5897362931070459152">"সক্ষম কৰিবলৈ টিপক"</string>
<string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"প্ৰশাসকৰ পিন দিয়ক"</string>
<string name="restr_pin_enter_pin" msgid="3395953421368476103">"পিন দিয়ক"</string>
@@ -1755,14 +1764,14 @@
<string name="immersive_cling_title" msgid="8394201622932303336">"স্ক্ৰীণ পূৰ্ণৰূপত চাই আছে"</string>
<string name="immersive_cling_description" msgid="3482371193207536040">"বাহিৰ হ\'বলৈ ওপৰৰপৰা তললৈ ছোৱাইপ কৰক।"</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"বুজি পালোঁ"</string>
- <string name="done_label" msgid="2093726099505892398">"সম্পন্ন কৰা হ\'ল"</string>
+ <string name="done_label" msgid="2093726099505892398">"সম্পন্ন কৰা হ’ল"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"ঘড়ীৰ বৃত্তাকাৰ শ্লাইডাৰ"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"মিনিটৰ বৃত্তাকাৰ শ্লাইডাৰ"</string>
<string name="select_hours" msgid="6043079511766008245">"ঘণ্টা বাছনি কৰক"</string>
<string name="select_minutes" msgid="3974345615920336087">"মিনিট বাছনি কৰক"</string>
<string name="select_day" msgid="7774759604701773332">"মাহ আৰু দিন বাছনি কৰক"</string>
<string name="select_year" msgid="7952052866994196170">"বছৰ বাছনি কৰক"</string>
- <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> মচা হ\'ল"</string>
+ <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> মচা হ’ল"</string>
<string name="managed_profile_label_badge" msgid="2355652472854327647">"কৰ্মস্থান <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"২য় কার্য <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"৩য় কার্য <xliff:g id="LABEL">%1$s</xliff:g>"</string>
@@ -1774,7 +1783,7 @@
<string name="package_deleted_device_owner" msgid="2307122077550236438">"আপোনাৰ প্ৰশাসকে মচিছে"</string>
<string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"আপোনাৰ বেটাৰিৰ অৱস্থা উন্নত কৰিবলৈ বেটাৰি সঞ্চয়কাৰীয়ে ডিভাইচৰ কিছুমান সুবিধা অফ কৰে আৰু এপসমূহক সীমিত কৰে। "<annotation id="url">"অধিক জানক"</annotation></string>
<string name="battery_saver_description" msgid="769989536172631582">"আপোনাৰ বেটাৰিৰ অৱস্থা উন্নত কৰিবলৈ বেটাৰি সঞ্চয়কাৰীয়ে ডিভাইচৰ কিছুমান সুবিধা অফ কৰে আৰু এপসমূহক সীমিত কৰে।"</string>
- <string name="data_saver_description" msgid="6015391409098303235">"ডেটা ব্য়ৱহাৰ মাত্ৰা কম কৰিবৰ বাবে ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা ব্য়ৱহাৰ কৰিব পাৰে, কিন্তু সঘনাই এই কার্য কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ এইয়ে হ\'ব পাৰে যে, উদাহৰণস্বৰূপে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ\'ব।"</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"ডেটা ব্য়ৱহাৰ মাত্ৰা কম কৰিবৰ বাবে ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা ব্য়ৱহাৰ কৰিব পাৰে, কিন্তু সঘনাই এই কার্য কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ এইয়ে হ\'ব পাৰে যে, উদাহৰণস্বৰূপে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ’ব।"</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"ডেটা সঞ্চয়কাৰী অন কৰিবনে?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"অন কৰক"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
@@ -1824,14 +1833,14 @@
<string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>এ কিছুমান ধ্বনি মিউট কৰি আছে"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে আৰু আপুনি ফেক্টৰী ডেটা ৰিছেট নকৰালৈকে ই সুস্থিৰভাৱে কাম নকৰিব পাৰে।"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে। সবিশেষ জানিবৰ বাবে আপোনাৰ ডিভাইচ নির্মাতাৰ সৈতে যোগাযোগ কৰক।"</string>
- <string name="stk_cc_ussd_to_dial" msgid="5214333646366591205">"USSD অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ\'ল"</string>
- <string name="stk_cc_ussd_to_ss" msgid="4884994189414782605">"USSD অনুৰোধ SS অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
- <string name="stk_cc_ussd_to_ussd" msgid="5728637484565449312">"নতুন USSD অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
- <string name="stk_cc_ussd_to_dial_video" msgid="4134455726513175559">"USSD SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ\'ল"</string>
- <string name="stk_cc_ss_to_dial" msgid="1360775164651754978">"SS অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ\'ল"</string>
- <string name="stk_cc_ss_to_dial_video" msgid="6577956662913194947">"SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ\'ল"</string>
- <string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS অনুৰোধ USSD অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
- <string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"নতুন SS অনুৰোধলৈ সলনি কৰা হ\'ল"</string>
+ <string name="stk_cc_ussd_to_dial" msgid="5214333646366591205">"USSD অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ’ল"</string>
+ <string name="stk_cc_ussd_to_ss" msgid="4884994189414782605">"USSD অনুৰোধ SS অনুৰোধলৈ সলনি কৰা হ’ল"</string>
+ <string name="stk_cc_ussd_to_ussd" msgid="5728637484565449312">"নতুন USSD অনুৰোধলৈ সলনি কৰা হ’ল"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="4134455726513175559">"USSD SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ’ল"</string>
+ <string name="stk_cc_ss_to_dial" msgid="1360775164651754978">"SS অনুৰোধ নিয়মীয়া কললৈ সলনি কৰা হ’ল"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="6577956662913194947">"SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ’ল"</string>
+ <string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS অনুৰোধ USSD অনুৰোধলৈ সলনি কৰা হ’ল"</string>
+ <string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"নতুন SS অনুৰোধলৈ সলনি কৰা হ’ল"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="notification_alerted_content_description" msgid="1296617716556420585">"সতৰ্ক কৰা হ’ল"</string>
<string name="expand_button_content_description_collapsed" msgid="3609784019345534652">"বিস্তাৰ কৰক"</string>
@@ -1846,8 +1855,8 @@
<string name="close_button_text" msgid="3937902162644062866">"বন্ধ কৰক"</string>
<string name="notification_messaging_title_template" msgid="3452480118762691020">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<plurals name="selected_count" formatted="false" msgid="7187339492915744615">
- <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ\'ল</item>
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ\'ল</item>
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item>
</plurals>
<string name="default_notification_channel_label" msgid="5929663562028088222">"শ্ৰেণীবদ্ধ নকৰা"</string>
<string name="importance_from_user" msgid="7318955817386549931">"এই জাননীবোৰৰ গুৰুত্ব আপুনি ছেট কৰব লাগিব।"</string>
@@ -1867,8 +1876,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"কৰ্মস্থানৰ প্ৰ\'ফাইল অন কৰিবনে?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"আপোনাৰ কৰ্মস্থানৰ এপসমূহ, জাননীসমূহ, ডেটা আৰু কৰ্মস্থানৰ প্ৰ\'ফাইলৰ অইন সুবিধাসমূহ অন কৰা হ\'ব"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"অন কৰক"</string>
- <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল, আৰু ই বিচৰাধৰণে কাম নকৰিবও পাৰে। ইয়াৰ আপডেট আছে নেকি চাওক, বা বিকাশকৰ্তাৰ সৈতে যোগাযোগ কৰক।"</string>
- <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"আপডেট আছে নেকি চাওক"</string>
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল, আৰু ই বিচৰাধৰণে কাম নকৰিবও পাৰে। ইয়াৰ আপডে’ট আছে নেকি চাওক, বা বিকাশকৰ্তাৰ সৈতে যোগাযোগ কৰক।"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"আপডে’ট আছে নেকি চাওক"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"আপুনি নতুন বার্তা লাভ কৰিছে"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"চাবলৈ এছএমএছ এপ্ খোলক"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"কিছুমান কৰ্মক্ষমতা সীমিত হ\'ব পাৰে"</string>
@@ -1884,7 +1893,7 @@
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="5268556852031489931">"ডেম\' আৰম্ভ কৰি থকা হৈছে…"</string>
<string name="demo_restarting_message" msgid="952118052531642451">"ডিভাইচটো আকৌ ছেটিং কৰি থকা হৈছে…"</string>
- <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>ক অক্ষম কৰা হ\'ল"</string>
+ <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>ক অক্ষম কৰা হ’ল"</string>
<string name="conference_call" msgid="3751093130790472426">"কনফাৰেঞ্চ কল"</string>
<string name="tooltip_popup_title" msgid="5253721848739260181">"টুলটিপ"</string>
<string name="app_category_game" msgid="5431836943981492993">"গেম"</string>
@@ -1916,10 +1925,10 @@
<string name="autofill_save_title_with_type" msgid="2339135393607143594"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g>ক ছেভ কৰিবনে?"</string>
<string name="autofill_save_title_with_2types" msgid="87616102361154432"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g>ক ছেভ কৰিবনে?"</string>
<string name="autofill_save_title_with_3types" msgid="4108978552969604555"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ক ছেভ কৰিবনে?"</string>
- <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ত আপডে\'ট কৰিবনে?"</string>
- <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g> আপডে\'ট কৰিবনে?"</string>
- <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g> আপডে\'ট কৰিবনে?"</string>
- <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"এই তথ্যবোৰ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ত আপডে\'ট কৰিবনে ?"</string>
+ <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ত আপডে’ট কৰিবনে?"</string>
+ <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g> আপডে’ট কৰিবনে?"</string>
+ <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g> আপডে’ট কৰিবনে?"</string>
+ <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"এই তথ্যবোৰ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ত আপডে’ট কৰিবনে ?"</string>
<string name="autofill_save_yes" msgid="6398026094049005921">"ছেভ কৰক"</string>
<string name="autofill_save_no" msgid="2625132258725581787">"নালাগে, ধন্যবাদ"</string>
<string name="autofill_update_yes" msgid="310358413273276958">"আপডে’ট কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 55966cf..076ea8d 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tətbiqə cihazın telefon funksiyalarına giriş icazəsi verir. Belə icazəli tətbiq bu telefonun nömrəsini və cihaz İD\'ni, zəngin aktiv olub-olmadığını və zəng edilən nömrəni müəyyən edə bilər."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"zəngləri sistem üzərindən yönləndirin"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tətbiqə, zəng təcrübəsini yaxşılaşdırmaq üçün, zəngləri sistem üzərindən yönləndirməyə icazə verilir."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"zənglərə sistemdə baxın və nəzarət edin."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Tətbiqin cihazda davam edən zəngləri görməsinə və nəzarət etməsinə icazə verin. Bura zəng edən nömrələr və zənglərin statusu haqqında məlumat daxildir."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"zəngə digər tətbiqdən davam edin"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Tətbiqə digər tətbiqdə başlayan zəngə davam etmək icazəsi verilir."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon nömrələrini oxuyun"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Tətbiqə Yaxın Məsafə Kommunikasiyası (NFC) teqləri, kartları və oxuyucuları ilə əlaqə qurmağa icazə verir."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"Ekran kilidini deaktiv edir"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Tətbiqə kilid açarını və təhlükəsizlik parolunu deaktiv etməyə imkan verir. Qanuni misal budur ki, telefon zəng qəbul edən zaman kilidi açır və zəng qurtarandan sonra kilidi bağlayır."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ekran kilidi mürəkkəbliliyini əldə edin və tələb edin"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Tətbiqə ekran kilidinin uzunluq intervalı və növünü göstərən ekran kilidi mürəkkəbliliyini (yüksək, orta, aşağı və ya heçbiri) öyrənməyə icazə verir. Tətbiq, istifadəçilərə ekran kilidini müəyyən səviyyəyə yeniləməyi təklif edə bilər, lakin istifadəçilər istənilən vaxt bunu iqnor edə bilər. Nəzərə alın ki, ekran kilidi şifrələndiyinə görə tətbiq parolu dəqiq bilmir."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"biometrik proqramdan istifadə edin"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Doğrulama üçün biometrik proqramdan istifadə etməyə imkan verir"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"barmaq izi avadanlığını idarə edin"</string>
@@ -1083,23 +1087,23 @@
<string name="deleteText" msgid="6979668428458199034">"Sil"</string>
<string name="inputMethod" msgid="1653630062304567879">"Daxiletmə metodu"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Mətn əməliyyatları"</string>
- <string name="email" msgid="4560673117055050403">"E-poçt"</string>
+ <string name="email" msgid="4560673117055050403">"E-poçtu aç"</string>
<string name="email_desc" msgid="3638665569546416795">"Seçilmiş ünvana e-məktub yazın"</string>
- <string name="dial" msgid="1253998302767701559">"Zəng"</string>
+ <string name="dial" msgid="1253998302767701559">"Zəng edin"</string>
<string name="dial_desc" msgid="6573723404985517250">"Seçilmiş telefon nömrəsinə zəng edin"</string>
- <string name="map" msgid="5441053548030107189">"Xəritə"</string>
+ <string name="map" msgid="5441053548030107189">"Xəritəni aç"</string>
<string name="map_desc" msgid="1836995341943772348">"Seçilmiş ünvanları tapın"</string>
<string name="browse" msgid="1245903488306147205">"Açın"</string>
<string name="browse_desc" msgid="8220976549618935044">"Seçilmiş linki açın"</string>
- <string name="sms" msgid="4560537514610063430">"Mesaj"</string>
+ <string name="sms" msgid="4560537514610063430">"Mesaj yazın"</string>
<string name="sms_desc" msgid="7526588350969638809">"Seçilmiş telefon nömrəsini mesajla göndərin"</string>
<string name="add_contact" msgid="7867066569670597203">"Əlavə edin"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Kontakta əlavə edin"</string>
<string name="view_calendar" msgid="979609872939597838">"Baxın"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Təqvimdə seçilmiş vaxta baxın"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Cədvəl"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Təqvimdə planlayın"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Tədbiri seçilmiş vaxta planlaşdırın"</string>
- <string name="view_flight" msgid="7691640491425680214">"Trek"</string>
+ <string name="view_flight" msgid="7691640491425680214">"İzləyin"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seçilmiş uçuşu izləyin"</string>
<string name="translate" msgid="9218619809342576858">"Tərcümə edin"</string>
<string name="translate_desc" msgid="4502367770068777202">"Seçilmiş mətni tərcümə edin"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Planşet yenilənir…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Cihaz yenilənir…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon başlayır…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android işə başlayır..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Planşet başlayır…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Cihaz başlayır…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Yaddaş optimallaşdırılır."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Bütün şəbəkələri görmək üçün klikləyin"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Qoşulun"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Bütün şəbəkələr"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> tərəfindən təklif edilən Wi‑Fi şəbəkəsinə qoşuldu"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> tərəfindən şəbəkələrin təklif edilməsinə icazə vermək itəyirsiniz?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Bəli"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Xeyr"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi avtomatik olaraq aktiv ediləcək"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Yadda saxlanmış yüksək keyfiyyətli şəbəkələr yaxınlıqda olduqda"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Yenidən aktiv etməyin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 9c45ba8..0a94acf 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -450,6 +450,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Dozvoljava aplikaciji da pristupa funkcijama telefona na uređaju. Ova dozvola omogućava aplikaciji da utvrdi broj telefona i ID-ove uređaja, zatim da li je poziv aktivan, kao i broj daljinskog uređaja sa kojim je uspostavljen poziv."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"preusmeravanje poziva preko sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Dozvoljava aplikaciji da preusmerava pozive preko sistema da bi poboljšala doživljaj pozivanja."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"pregled i kontrola poziva preko sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Dozvoljava aplikaciji da pregleda i kontroliše trenutne pozive na uređaju. To obuhvata informacije poput brojeva telefona i statusa poziva."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"nastavi poziv u drugoj aplikaciji"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Dozvoljava aplikaciji da nastavi poziv koji je započet u drugoj aplikaciji."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čitanje brojeva telefona"</string>
@@ -510,6 +512,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Dozvoljava aplikaciji da komunicira sa oznakama, karticama i čitačima komunikacije kratkog dometa (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"onemogućavanje zaključavanja ekrana"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Dozvoljava aplikaciji da onemogući zaključavanje tastature i sve povezane bezbednosne mere sa lozinkama. Na primer, telefon onemogućava zaključavanje tastature pri prijemu dolaznog telefonskog poziva, a zatim ga ponovo omogućava po završetku poziva."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dobijanje i traženje nivoa složenosti zaključavanja ekrana"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Dozvoljava aplikaciji da sazna nivo složenosti zaključavanja ekrana (visoka, srednja, niska ili nijedna), što ukazuje na mogući opseg trajanja i tip zaključavanja ekrana. Aplikacija može i da predlaže korisnicima da ažuriraju zaključavanje ekrana na određeni nivo, ali korisnici slobodno mogu da zanemare to i da idu na druge stranice. Imajte na umu da se podaci za zaključavanje ekrana ne čuvaju kao običan tekst, pa aplikacija ne zna tačnu lozinku."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"koristi biometrijski hardver"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Dozvoljava aplikaciji da koristi biometrijski hardver za potvrdu identiteta"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljaj hardverom za otiske prstiju"</string>
@@ -1107,7 +1111,7 @@
<string name="email_desc" msgid="3638665569546416795">"Pošaljite imejl na izabranu adresu"</string>
<string name="dial" msgid="1253998302767701559">"Pozovi"</string>
<string name="dial_desc" msgid="6573723404985517250">"Pozovite izabrani broj telefona"</string>
- <string name="map" msgid="5441053548030107189">"Mapa"</string>
+ <string name="map" msgid="5441053548030107189">"Prikaži na mapi"</string>
<string name="map_desc" msgid="1836995341943772348">"Pronađite izabranu adresu"</string>
<string name="browse" msgid="1245903488306147205">"Otvori"</string>
<string name="browse_desc" msgid="8220976549618935044">"Otvorite izabrani URL"</string>
@@ -1201,6 +1205,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet se ažurira…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Uređaj se ažurira…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se pokreće…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se pokreće…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet se pokreće…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Uređaj se pokreće…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Memorija se optimizuje."</string>
@@ -1261,6 +1266,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dodirnite da biste videli sve mreže"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Poveži"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sve mreže"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Povezani ste sa Wi‑Fi mrežom koju predlaže <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Želite li da dozvolite da vam <xliff:g id="NAME">%s</xliff:g> predlaže mreže?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi će se automatski uključiti"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kada ste u blizini sačuvane mreže visokog kvaliteta"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne uključuj ponovo"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 930feba..cfe076d 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дазваляе прыкладанням атрымлiваць доступ да функцый тэлефона на прыладзе. Дзякуючы гэтаму дазволу прыкладанне можа вызначаць iдэнтыфiкатары нумару тэлефона i прылады, незалежна ад таго, цi актыўны выклiк, i аддалены нумар, на якi робiцца выклiк."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"перанакіраванне выклікаў праз сістэму"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дазваляе праграме перанакіроўваць выклікі праз сістэму ў мэтах паляпшэння выклікаў."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"праглядаць выклікі і кіраваць імі праз сістэму."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Дазваляе праграме праглядаць на прыладзе ўваходныя выклікі і кіраваць імі. Гэта інфармацыя ўключае нумары выклікаў і звесткі пра іх краіну."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"працяг выкліку з іншай праграмы"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Дазваляе праграме працягваць выклік, які пачаўся ў іншай праграме."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"счытваць нумары тэлефонаў"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Дазваляе прыкладаннzv спалучацца з тэгамі, картамі і счытваючымі прыладамі Near Field Communication (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"адключэнне блакiроўкi экрана"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Дазваляе прыкладанням адключаць блакiроўку клавіятуры і любыя сродкі абароны, звязаныя з паролем. Прыкладам гэтага з\'яўляецца адключэнне тэлефонам блакiроўкi клавіятуры пры атрыманні ўваходнага выкліку і паўторнае ўключэнне блакiроўкi клавіятуры, калі выклік завершаны."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"атрымліваць і адсылаць запыты наконт узроўню складанасці блакіроўкі экрана"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Дазваляе праграме вызначаць узровень складанасці блакіроўкі экрана (высокі, сярэдні, нізкі ці нулявы), які залежыць ад даўжыні пароля і ад тыпу блакіроўкі экрана. Праграма можа прапанаваць карыстальнікам ускладніць блакіроўку экрана, аднак гэту прапанову можна ігнараваць. Заўважце, што праграма не можа ведаць тып і пароль блакіроўкі экрана, таму што яны захоўваюцца ў зашыфраваным выглядзе."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"выкарыстоўваць біяметрычнае абсталяванне"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Дазваляе праграме выкарыстоўваць для аўтэнтыфікацыі біяметрычнае абсталяванне"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"кіраваць апаратнымі сродкамі для адбіткаў пальцаў"</string>
@@ -1123,21 +1127,21 @@
<string name="deleteText" msgid="6979668428458199034">"Выдалiць"</string>
<string name="inputMethod" msgid="1653630062304567879">"Метад уводу"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Дзеянні з тэкстам"</string>
- <string name="email" msgid="4560673117055050403">"Электронная пошта"</string>
+ <string name="email" msgid="4560673117055050403">"Напісаць ліст"</string>
<string name="email_desc" msgid="3638665569546416795">"Напісаць электронны ліст на выбраны адрас"</string>
<string name="dial" msgid="1253998302767701559">"Выклікаць"</string>
<string name="dial_desc" msgid="6573723404985517250">"Звязацца з абанентам"</string>
- <string name="map" msgid="5441053548030107189">"Карта"</string>
+ <string name="map" msgid="5441053548030107189">"Адкрыць карту"</string>
<string name="map_desc" msgid="1836995341943772348">"Паказаць выбраны адрас на карце"</string>
<string name="browse" msgid="1245903488306147205">"Адкрыць"</string>
<string name="browse_desc" msgid="8220976549618935044">"Адкрыць URL у браўзеры"</string>
- <string name="sms" msgid="4560537514610063430">"Паведамленне"</string>
+ <string name="sms" msgid="4560537514610063430">"Напісаць SMS"</string>
<string name="sms_desc" msgid="7526588350969638809">"Адправіць паведамленне на выбраны нумар"</string>
<string name="add_contact" msgid="7867066569670597203">"Дадаць"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Дадаць у кантакты"</string>
<string name="view_calendar" msgid="979609872939597838">"Прагледзець"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Паказаць выбраны час у календары"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Графік"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Запланаваць"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Запланаваць падзею ў выбраны час"</string>
<string name="view_flight" msgid="7691640491425680214">"Сачыць"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Адсочваць рэйс"</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Абнаўленне планшэта…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Абнаўленне прылады…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Запуск тэлефона…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android запускаецца..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Запуск планшэта…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Запуск прылады…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Аптымізацыя сховішча."</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Дакраніцеся, каб убачыць усе сеткі"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Падключыцца"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Усе сеткі"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Падключана да сеткі Wi‑Fi, прапанаванай праграмай \"<xliff:g id="NAME">%s</xliff:g>\""</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Хочаце дазволіць праграме \"<xliff:g id="NAME">%s</xliff:g>\" прапаноўваць вам сеткі?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Так"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi уключыцца аўтаматычна"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Побач з захаванай сеткай з высакаякасным сігналам"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не ўключаць зноў"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d2857e8..8897bac 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Разрешава на приложението достъп до телефонните функции на устройството. Това разрешение позволява на приложението да определя телефонния номер и идентификационния номер на устройството, дали се води разговор и отдалечения номер, до който е установена връзка с обаждането."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"маршрутизиране на обажданията чрез системата"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Разрешава на приложението да маршрутизира обажданията си чрез системата с цел подобряване на свързаната с тях практическа работа."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"вижда и управлява обажданията чрез системата."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Разрешава на приложението да вижда и управлява текущите обаждания на устройството. Това включва различна информация, като например номерата и състоянието на обажданията."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"продължаване на обаждане от друго приложение"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Разрешава на приложението да продължи обаждане, стартирано в друго приложение."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"четене на телефонните номера"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Разрешава на приложението да комуникира с маркери, карти и четци, ползващи комуникация в близкото поле (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"деактивиране на заключването на екрана ви"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Разрешава на приложението да деактивира заключването на клавиатурата и свързаната защита с парола. Например телефонът деактивира заключването при получаване на входящо обаждане и после го активира отново, когато обаждането завърши."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"получаване и заявяване на сложността на опцията за заключване на екрана"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Разрешава на приложението да разбере нивото на сложност на опцията за заключване на екрана (високо, средно, ниско или липса на такова), което указва възможния диапазон на дължината и типа на опцията. Приложението може също да предложи на потребителите да актуализират опцията за заключване на екрана до определено ниво, но те могат да пренебрегнат това и да излязат от него. Обърнете внимание, че опцията за заключване на екрана не се съхранява като обикновен текст, така че приложението не знае точната парола."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"използване на хардуера за биометрични данни"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Разрешава на приложението да използва хардуера за биометрични данни с цел удостоверяване"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"управление на хардуера за отпечатъци"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблетът се актуализира…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Устройството се актуализира…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Телефонът се стартира…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android се стартира…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблетът се стартира…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Устройството се стартира…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хранилището се оптимизира."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Докоснете, за да видите всички мрежи"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Свързване"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Всички мрежи"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Установена е връзка с Wi‑Fi мрежа, предложена от <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Искате ли да разрешите на <xliff:g id="NAME">%s</xliff:g> да ви предлага мрежи?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ще се включи автоматично"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Когато сте в района на запазена мрежа с високо качество"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Без повторно включване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 80eefc4..26a3ba0 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -318,7 +318,7 @@
<string name="permgroupdesc_visual" msgid="3415827902566663546">"আপনার ফটো ও ভিডিও অ্যাক্সেস করুন"</string>
<string name="permgrouprequest_visual" msgid="6907523945030290376">"আপনার ফটো ও ভিডিওতে <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে অ্যাক্সেস দেবেন?"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"উইন্ডোর কন্টেন্ট পুনরুদ্ধার করে"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"আপনি ইন্টারঅ্যাক্ট করছেন এমন একটি উইন্ডোর সামগ্রীকে সযত্নে নিরীক্ষণ করে৷"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"ব্যবহার করছেন এমন একটি উইন্ডোর কন্টেন্ট নিরীক্ষণ করে৷"</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"স্পর্শের মাধ্যমে অন্বেষণ করা চালু করুন"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"যে আইটেমগুলিতে আলতো চেপেছেন সেগুলি সশব্দে বলবে এবং ইঙ্গিতগুলি ব্যবহার করে স্ক্রিন অন্বেষণ করা যাবে৷"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপনার লেখা পাঠ্যকে নিরীক্ষণ করে"</string>
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"অ্যাপ্লিকেশানটিকে ডিভাইসের ফোন বৈশিষ্ট্যগুলিকে অ্যাক্সেস করার অনুমতি দেয়৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে একটি কল সক্রিয় থাকা অবস্থায় এবং দূরবর্তী নম্বর একটি কল দ্বারা সংযুক্ত থাকাকালীনও ফোন নম্বর এবং ডিভাইসের IDগুলি নির্ধারণ করার অনুমতি দেয়৷"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"সিস্টেমের মাধ্যমে কলগুলি রুট করতে দিন"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"কল করার অভিজ্ঞতা উন্নত করার জন্য অ্যাপকে সিস্টেমের মাধ্যমে তার কলগুলি রুট করতে দেয়।"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"সিস্টেমের মাধ্যমে কল দেখা এবং নিয়ন্ত্রণ করা।"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ডিভাইসে চালু আছে এমন কল দেখতে এবং নিয়ন্ত্রণ করতে অ্যাপকে অনুমতি দেয়। কল করা হচ্ছে যে নম্বরে সেটি এবং কলের স্ট্যাটাস কী সেই সব তথ্য এতে অন্তর্ভুক্ত।"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"অন্য কোনও অ্যাপ দিয়ে করে থাকা কল চালিয়ে যান"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"অন্য কোনও অ্যাপ দিয়ে কল করলে এই অ্যাপটিকে সেটি চালিয়ে যেতে দেয়।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ফোন নম্বরগুলি পড়া হোক"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"অ্যাপ্লিকেশানকে নিয়ার ফিল্ড কমিউনিকেশন (NFC) ট্যাগ, কার্ড এবং রিডারগুলির সাথে যোগাযোগ করতে দেয়৷"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"আপনার স্ক্রিন লক অক্ষম করুন"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"কী-লক এবং যেকোনো সংশ্লিষ্ট পাসওয়ার্ড সুরক্ষা অক্ষম করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ উদাহরণস্বরূপ, একটি ইনকামিং ফোন কল গ্রহণ করার সময়ে ফোনটি কী-লক অক্ষম করে, তারপরে কল শেষ হয়ে গেলে কী-লকটিকে আবার সক্ষম করে৷"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"স্ক্রিন লক কমপ্লেক্সিটি প্যাটার্নটির জন্য অনুরোধ জানান এবং ব্যবহার করুন"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"স্ক্রিন লক কমপ্লেক্সিটি প্যাটার্নটির লেভেল (বেশি, মাঝারি, কম বা কোনও কিছুই নেই) জানতে অ্যাপকে অনুমতি দিন, যা সম্ভাব্য স্ক্রিন লকের ধরন এবং তা কতটা দীর্ঘ হবে তার ইঙ্গিত দেয়। একটি নির্দিষ্ট লেভেল পর্যন্ত স্ক্রিন লক আপডেট করা যাবে তাও এই অ্যাপটি সাজেস্ট করতে পারে, তবে ব্যবহারকারীর তা উপেক্ষা করার এবং অন্য কোথাও চলে যাওয়ার স্বাধীনতা আছে। মনে রাখবেন প্লেনটেক্সটে স্ক্রিন লক স্টোর করা হয় না, তাই সঠিক পাসওয়ার্ড অ্যাপের পক্ষে জানা সম্ভব নয়।"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করুন"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"অ্যাপটিকে যাচাইকরণের জন্য বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করার অনুমতি দেয়"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার পরিচালনা করুন"</string>
@@ -1083,23 +1087,23 @@
<string name="deleteText" msgid="6979668428458199034">"মুছুন"</string>
<string name="inputMethod" msgid="1653630062304567879">"ইনপুট পদ্ধতি"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"পাঠ্য ক্রিয়াগুলি"</string>
- <string name="email" msgid="4560673117055050403">"ইমেল"</string>
+ <string name="email" msgid="4560673117055050403">"ইমেল করুন"</string>
<string name="email_desc" msgid="3638665569546416795">"বেছে নেওয়া আইডিতে ইমেল পাঠান"</string>
- <string name="dial" msgid="1253998302767701559">"কল"</string>
+ <string name="dial" msgid="1253998302767701559">"কল খুলুন"</string>
<string name="dial_desc" msgid="6573723404985517250">"বেছে নেওয়া ফোন নম্বরে কল করুন"</string>
- <string name="map" msgid="5441053548030107189">"ম্যাপ"</string>
+ <string name="map" msgid="5441053548030107189">"ম্যাপ খুলুন"</string>
<string name="map_desc" msgid="1836995341943772348">"বেছে নেওয়া ঠিকানাটি ম্যাপে দেখুন"</string>
<string name="browse" msgid="1245903488306147205">"খুলুন"</string>
- <string name="browse_desc" msgid="8220976549618935044">"বেছে নেওয়া ইউআরএলে যান"</string>
- <string name="sms" msgid="4560537514610063430">"মেসেজ"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"বেছে নেওয়া ইউআরএল-এ যান"</string>
+ <string name="sms" msgid="4560537514610063430">"মেসেজ করুন"</string>
<string name="sms_desc" msgid="7526588350969638809">"বেছে নেওয়া ফোন নম্বরে মেসেজ পাঠান"</string>
<string name="add_contact" msgid="7867066569670597203">"যোগ করুন"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"পরিচিতিতে যোগ করুন"</string>
<string name="view_calendar" msgid="979609872939597838">"দেখুন"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"বেছে নেওয়া দিনটি ক্যালেন্ডারে দেখুন"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"সময়সূচি"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"সময়সূচি সেট করুন"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"বেছে নেওয়া সময়ে ইভেন্ট সেট করুন"</string>
- <string name="view_flight" msgid="7691640491425680214">"ট্র্যাক"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ট্র্যাক করুন"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"বেছে নেওয়া ফ্লাইট ট্র্যাক করুন"</string>
<string name="translate" msgid="9218619809342576858">"অনুবাদ করুন"</string>
<string name="translate_desc" msgid="4502367770068777202">"বেছে নেওয়া টেক্সট অনুবাদ করুন"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ট্যাবলেট আপডেট করা হচ্ছে…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ডিভাইস আপডেট করা হচ্ছে…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ফোন চালু করা হচ্ছে…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android চালু হচ্ছে…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ট্যাবলেট চালু করা হচ্ছে…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ডিভাইস চালু করা হচ্ছে…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"স্টোরেজ অপ্টিমাইজ করা হচ্ছে৷"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"সমস্ত নেটওয়ার্ক দেখতে ট্যাপ করুন"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"সংযুক্ত করুন"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"সব নেটওয়ার্ক"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>-এর সাজেস্ট করা ওয়াই-ফাইয়ে নেটওয়ার্ক কানেক্ট করা হয়েছে"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"আপনি <xliff:g id="NAME">%s</xliff:g>-কে নেটওয়ার্ক সাজেস্ট করতে দিতে চান?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"হ্যাঁ"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"না"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ওয়াই-ফাই অটোমেটিক চালু হবে"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"যখন আপনি একটি উচ্চ মানের সংরক্ষিত নেটওয়ার্ক কাছাকাছি থাকেন"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"আবার চালু করবেন না"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 718cba4..e336cd0 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -450,6 +450,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Omogućava aplikaciji pristup telefonskim funkcijama uređaja. Ovo odobrenje omogućava aplikaciji određivanje telefonskog i identifikacionog broja uređaja, bez obzira da li je poziv aktivan i da li je uspostavljena veza sa pozivanim brojem."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"usmjeravanje poziva preko sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Dopušta aplikaciji da pozive usmjeri preko sistema radi poboljšanja iskustva pozivanja."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"vidjeti i kontrolirati pozive preko sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Dozvoljava aplikaciji da vidi i kontrolira odlazne pozive na uređaju. To uključuje informacije kao što su brojevi telefona i stanja poziva."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"nastavlja poziv iz druge aplikacije"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Dozvoljava aplikaciji nastavljanje poziva koji je započet u drugoj aplikaciji."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čitanje telefonskih brojeva"</string>
@@ -510,6 +512,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Dozvoljava aplikaciji komuniciranje sa NFC (komunikacija bliskog polja) oznakama, karticama i čitačima."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivacija zaključavanja ekrana"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Omogućava aplikaciji deaktivaciju zaključane tastature i svih povezanih zaštita. Naprimjer, telefon deaktivira zaključavanje tastature kod dolaznog telefonskog poziva, a zatim ponovo aktivira zaključavanje tastature kada je poziv završen."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"preuzimanje i traženje nivoa kompleksnosti zaključavanja ekrana"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Omogućava aplikaciji da sazna nivo kompleksnosti zaključavanja ekrana (visoki, srednji, niski ili bez zaključavanja), što naznačava mogući raspon trajanja i vrste zaključavanja ekrana. Aplikacija također može korisnicima predložiti da ažuriraju zaključavanje ekrana do određenog nivoa ali korisnici slobodno mogu ignorirati prijedlog i napustiti stranicu. Važno je napomenuti da se zaključavanje ekrana ne pohranjuje kao obični tekst tako da aplikacija ne zna tačnu lozinku."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"koristi biometrijski hardver za otiske prstij"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Omogućava aplikaciji da za autentifikaciju koristi biometrijski hardver"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje hardverom za otiske prstiju"</string>
@@ -1103,28 +1107,28 @@
<string name="deleteText" msgid="6979668428458199034">"Izbriši"</string>
<string name="inputMethod" msgid="1653630062304567879">"Način unosa"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Akcije za tekst"</string>
- <string name="email" msgid="4560673117055050403">"E-pošta"</string>
- <string name="email_desc" msgid="3638665569546416795">"E-pošta odabrane adrese"</string>
- <string name="dial" msgid="1253998302767701559">"Pozovite"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Pozovite odabrani broj telefona"</string>
- <string name="map" msgid="5441053548030107189">"Mapa"</string>
- <string name="map_desc" msgid="1836995341943772348">"Lociranje odabrane adrese"</string>
- <string name="browse" msgid="1245903488306147205">"Otvorite"</string>
- <string name="browse_desc" msgid="8220976549618935044">"Otvorite odabrani URL"</string>
- <string name="sms" msgid="4560537514610063430">"Poruka"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Pošaljite poruku odabranom broju telefona"</string>
- <string name="add_contact" msgid="7867066569670597203">"Dodajte"</string>
+ <string name="email" msgid="4560673117055050403">"Pošalji e-poruku"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Pošalji e-poruku na odabranu adresu"</string>
+ <string name="dial" msgid="1253998302767701559">"Pozovi"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Pozovi odabrani broj telefona"</string>
+ <string name="map" msgid="5441053548030107189">"Prikaži na mapi"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Lociraj odabranu adresu"</string>
+ <string name="browse" msgid="1245903488306147205">"Otvori"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"Otvori odabrani URL"</string>
+ <string name="sms" msgid="4560537514610063430">"Pošalji SMS"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Pošalji SMS odabranom broju telefona"</string>
+ <string name="add_contact" msgid="7867066569670597203">"Dodaj"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Dodaj u kontakte"</string>
<string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Pogledajte odabrano vrijeme u kalendaru"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaži odabrano vrijeme u kalendaru"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Zakaži"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakažite događaj za odabrano vrijeme"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakaži događaj za odabrano vrijeme"</string>
<string name="view_flight" msgid="7691640491425680214">"Prati"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Pratite odabrani let"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Prati odabrani let"</string>
<string name="translate" msgid="9218619809342576858">"Prevedi"</string>
<string name="translate_desc" msgid="4502367770068777202">"Prevedi odabrani tekst"</string>
- <string name="define" msgid="7394820043869954211">"Definirajte"</string>
- <string name="define_desc" msgid="7910883642444919726">"Definiranje odabranog teksta"</string>
+ <string name="define" msgid="7394820043869954211">"Definiraj"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definiraj odabrani tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke funkcije sistema možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1203,6 +1207,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Ažuriranje tableta…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Ažuriranje uređaja…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Pokretanje telefona…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se pokreće..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Pokretanje tableta…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Pokretanje uređaja…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje pohrane."</string>
@@ -1263,6 +1268,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dodirnite da vidite sve mreže"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Povežite se"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sve mreže"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Povezani ste na WiFi mrežu koju je predložila aplikacija <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Želite li dozvoliti aplikaciji <xliff:g id="NAME">%s</xliff:g> da vam predlaže mreže?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi veza će se automatski uključiti"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kada ste u blizini sačuvane mreže visokog kvaliteta"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nemoj ponovo uključiti"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index c68e524..35c223b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet que l\'aplicació accedeixi a les funcions de telèfon del dispositiu. Aquest permís permet que l\'aplicació determini el número de telèfon i els identificadors del dispositiu, si hi ha una trucada activa i el número remot connectat amb una trucada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminar trucades a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permet que l\'aplicació encamini les trucades a través del sistema per millorar-ne l\'experiència."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"consulta i controla les trucades a través del sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permet que l\'aplicació consulti i controli les trucades en curs al dispositiu. Inclou informació com ara l\'estat i els números de les trucades."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"Continua una trucada d\'una altra aplicació"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permet que l\'aplicació continuï una trucada que s\'havia iniciat en una altra aplicació."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"llegir els números de telèfon"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permet que l\'aplicació es comuniqui amb les etiquetes, les targetes i els lectors de Comunicació de camp proper (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"desactivació del bloqueig de pantalla"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permet que l\'aplicació desactivi el bloqueig del teclat i qualsevol element de seguretat de contrasenyes associat. Per exemple, el telèfon desactiva el bloqueig del teclat en rebre una trucada entrant i, a continuació, reactiva el bloqueig del teclat quan finalitza la trucada."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtenir i sol·licitar el nivell de complexitat del bloqueig de pantalla"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permet que l\'aplicació conegui el nivell de complexitat del bloqueig de pantalla (alt, mitjà, baix o cap), que indica la llargària i el tipus de bloqueig de pantalla possibles. L\'aplicació també pot suggerir que els usuaris actualitzin el bloqueig de pantalla a un nivell determinat, però els usuaris poden ignorar aquestes recomanacions. Tingues en compte que el bloqueig de pantalla no s\'emmagatzema com a text sense format, de manera que l\'aplicació no coneix la contrasenya exacta."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"utilitza maquinari biomètric"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permet que l\'aplicació faci servir maquinari biomètric per a l\'autenticació"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"Gestionar el maquinari d\'empremtes digitals"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"Suprimeix"</string>
<string name="inputMethod" msgid="1653630062304567879">"Mètode d\'introducció de text"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Accions de text"</string>
- <string name="email" msgid="4560673117055050403">"Correu electrònic"</string>
+ <string name="email" msgid="4560673117055050403">"Envia un correu"</string>
<string name="email_desc" msgid="3638665569546416795">"Envia un correu electrònic a l\'adreça seleccionada"</string>
<string name="dial" msgid="1253998302767701559">"Truca"</string>
<string name="dial_desc" msgid="6573723404985517250">"Truca al número de telèfon seleccionat"</string>
@@ -1091,12 +1095,12 @@
<string name="map_desc" msgid="1836995341943772348">"Localitza l\'adreça seleccionada"</string>
<string name="browse" msgid="1245903488306147205">"Obre"</string>
<string name="browse_desc" msgid="8220976549618935044">"Obre l\'URL seleccionat"</string>
- <string name="sms" msgid="4560537514610063430">"Missatge"</string>
+ <string name="sms" msgid="4560537514610063430">"Envia un SMS"</string>
<string name="sms_desc" msgid="7526588350969638809">"Envia un SMS al número de telèfon seleccionat"</string>
<string name="add_contact" msgid="7867066569670597203">"Afegeix"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Afegeix als contactes"</string>
<string name="view_calendar" msgid="979609872939597838">"Mostra"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Consulta l\'hora seleccionada al calendari"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Consulta la data seleccionada al calendari"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Programa"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programa un esdeveniment per a la data seleccionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Fes un seguiment"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"La tauleta s\'està actualitzant…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"El dispositiu s\'està actualitzant…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"S\'està iniciant el telèfon…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"S\'està iniciant Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"S\'està iniciant la tauleta…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"S\'està iniciant el dispositiu…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"S\'està optimitzant l\'emmagatzematge."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toca per veure totes les xarxes"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connecta"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Totes les xarxes"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"T\'has connectat a la xarxa Wi‑Fi proposada per l\'aplicació <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vols permetre que <xliff:g id="NAME">%s</xliff:g> et proposi xarxes?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sí"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"La Wi-Fi s\'activarà automàticament"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quan siguis a prop d\'una xarxa de qualitat desada"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"No tornis a activar"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4758c07..0061ca7 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Umožňuje aplikaci získat přístup k telefonním funkcím zařízení. Toto oprávnění umožňuje aplikaci zjistit telefonní číslo telefonu, identifikační čísla zařízení, zda zrovna probíhá hovor, a vzdálené číslo, ke kterému je hovor připojen."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"směrování volání prostřednictvím systému"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Umožňuje aplikaci směrovat volání prostřednictvím systému za účelem vylepšení funkcí volání."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"zobrazení a ovládání hovorů v systému."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Umožňuje aplikaci zobrazit a ovládat probíhající hovory v zařízení. Zahrnuje to informace jako zúčastněna čísla a stav hovoru."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"pokračování v hovoru v jiné aplikaci"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Umožňuje aplikace pokračovat v hovoru, který byl zahájen v jiné aplikaci."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"přístup k telefonním číslům"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Umožňuje aplikaci komunikovat se štítky, kartami a čtečkami s podporou technologie NFC."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"vypnutí zámku obrazovky"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Umožňuje aplikaci vypnout zámek kláves a související zabezpečení heslem. Telefon například vypne zámek klávesnice při příchozím hovoru a po skončení hovoru jej zase zapne."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"zjišťování a vyžadování složitosti zámku obrazovky"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Umožňuje aplikaci zjistit úroveň složitosti zámku obrazovky (vysoká, střední, nízká nebo žádná), která ukazuje možnou délku a typ zámku obrazovky. Aplikace také může uživatelům navrhovat, aby zámek obrazovky upravili na určitou úroveň, ale uživatelé mohou návrhy klidně ignorovat a odejít. Zámek obrazovky není uložen jako prostý text, a tak aplikace přesné heslo nezná."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"použití biometrického hardwaru"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Umožňuje aplikaci použít k ověření biometrický hardware"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"správa hardwaru na čtení otisků prstů"</string>
@@ -1131,7 +1135,7 @@
<string name="map_desc" msgid="1836995341943772348">"Vyhledat vybranou adresu"</string>
<string name="browse" msgid="1245903488306147205">"Otevřít"</string>
<string name="browse_desc" msgid="8220976549618935044">"Otevřít vybranou adresu URL"</string>
- <string name="sms" msgid="4560537514610063430">"Zpráva"</string>
+ <string name="sms" msgid="4560537514610063430">"Napsat zprávu"</string>
<string name="sms_desc" msgid="7526588350969638809">"Napsat SMS na vybrané telefonní číslo"</string>
<string name="add_contact" msgid="7867066569670597203">"Přidat"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Přidat do kontaktů"</string>
@@ -1144,7 +1148,7 @@
<string name="translate" msgid="9218619809342576858">"Přeložit"</string>
<string name="translate_desc" msgid="4502367770068777202">"Přeložit vybraný text"</string>
<string name="define" msgid="7394820043869954211">"Definovat"</string>
- <string name="define_desc" msgid="7910883642444919726">"Definuje vybraný text"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definovat vybraný text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet se aktualizuje…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Zařízení se aktualizuje…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se spouští…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Spouštění systému Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet se spouští…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Zařízení se spouští…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Probíhá optimalizace úložiště."</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Klepnutím zobrazíte všechny sítě"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Připojit"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Všechny sítě"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Připojeno k síti Wi-Fi navrhované aplikací <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Chcete aplikaci <xliff:g id="NAME">%s</xliff:g> povolit, aby vám navrhovala sítě?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ano"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se zapne automaticky"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Když budete v dosahu kvalitní uložené sítě"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Znovu nezapínat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0167acf..2795efe 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillader, at appen kan få adgang til telefonfunktionerne på enheden. Med denne tilladelse kan appen fastslå telefonnummeret og enheds-id\'erne, hvorvidt et opkald er aktivt samt det eksterne nummer, der oprettes forbindelse til via et opkald."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"dirigere opkald gennem systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tillader appen at dirigere sine opkald gennem systemet for at forbedre opkaldsoplevelsen."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"se og styre opkald via systemet."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Tillader, at appen kan se og styre igangværende opkald på enheden. Dette omfatter oplysninger såsom telefonnumre og status for opkaldene."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"fortsætte et opkald fra en anden app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Tillader, at appen fortsætter et opkald, der blev startet i en anden app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"læse telefonnumre"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Tillader, at appen kan kommunikere med NFC-tags (Near Field Communication), -kort og -læsere."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivere din skærmlås"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Tillader, at appen kan deaktivere tastaturlåsen og anden form for tilknyttet adgangskodesikkerhed. Telefonen deaktiverer f.eks. tastaturlåsen ved indgående telefonopkald og aktiverer tastaturlåsen igen, når opkaldet er afsluttet."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"få og anmode om skærmlåsens kompleksitet"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Giver appen tilladelse til at kende skærmlåsens kompleksitet (høj, medium, lav eller ingen), hvilket indikerer skærmlåsens mulige længde og type. Appen kan også foreslå brugerne at opdatere deres skærmlås til et bestemt niveau, men brugerne kan frit ignorere det og gå videre. Bemærk! Skærmlåsen gemmes ikke som almindelig tekst, så appen kender ikke adgangskoden."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"brug biometrisk hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Tillader, at appen kan bruge biometrisk hardware til godkendelse"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrer fingeraftrykhardware"</string>
@@ -1083,15 +1087,15 @@
<string name="deleteText" msgid="6979668428458199034">"Slet"</string>
<string name="inputMethod" msgid="1653630062304567879">"Inputmetode"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
- <string name="email" msgid="4560673117055050403">"E-mail"</string>
+ <string name="email" msgid="4560673117055050403">"Send mail"</string>
<string name="email_desc" msgid="3638665569546416795">"Send en mail til den valgte adresse"</string>
<string name="dial" msgid="1253998302767701559">"Ring op"</string>
<string name="dial_desc" msgid="6573723404985517250">"Ring til det valgte telefonnummer"</string>
- <string name="map" msgid="5441053548030107189">"Kort"</string>
+ <string name="map" msgid="5441053548030107189">"Åbn kort"</string>
<string name="map_desc" msgid="1836995341943772348">"Find den valgte adresse"</string>
<string name="browse" msgid="1245903488306147205">"Åbn"</string>
<string name="browse_desc" msgid="8220976549618935044">"Åbn den valgte webadresse"</string>
- <string name="sms" msgid="4560537514610063430">"Besked"</string>
+ <string name="sms" msgid="4560537514610063430">"Send besked"</string>
<string name="sms_desc" msgid="7526588350969638809">"Send en besked til det valgte telefonnummer"</string>
<string name="add_contact" msgid="7867066569670597203">"Tilføj"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Føj til kontaktpersoner"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Denne tablet opdaterer…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Enheden opdaterer…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonen starter…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android starter..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Denne tablet starter…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Enheden starter…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lageret optimeres."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tryk for at se alle netværk"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Opret forbindelse"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle netværk"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Forbundet til Wi‑Fi-netværket, som <xliff:g id="NAME">%s</xliff:g> har foreslået"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vil du tillade, at <xliff:g id="NAME">%s</xliff:g> foreslår netværk?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nej"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi aktiveres automatisk"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Når du er i nærheden af et gemt netværk af høj kvalitet"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Aktivér ikke igen"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a14a871..6087438 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -324,7 +324,7 @@
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Text bei der Eingabe beobachten"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Einschließlich personenbezogener Daten wie Kreditkartennummern und Passwörter."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displayvergrößerung festlegen"</string>
- <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe des Displays und die Zoom-Position auf dem Display fest."</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe und -Position auf dem Display fest."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Bewegungen möglich"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Tippen, Wischen, Zusammenziehen und andere Bewegungen möglich."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Bewegungen auf dem Fingerabdrucksensor"</string>
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ermöglicht der App, auf die Telefonfunktionen des Geräts zuzugreifen. Die Berechtigung erlaubt der App, die Telefonnummer und Geräte-IDs zu erfassen, festzustellen, ob gerade ein Gespräch geführt wird, und die Rufnummer verbundener Anrufer zu lesen."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"Anrufe über das System durchführen"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ermöglicht der App, Anrufe über das System durchzuführen, um die Anrufqualität zu verbessern."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Anrufe durch das System einsehen und verwalten."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ermöglicht der App, aktuelle Anrufe einzusehen und zu verwalten. Dies beinhaltet Informationen wie Nummern für Anrufe und den Status der Anrufe."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"Anruf aus einer anderen App weiterführen"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ermöglicht der App, einen Anruf weiterzuführen, der in einer anderen App begonnen wurde."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"Telefonnummern vorlesen"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Ermöglicht der App die Kommunikation mit Tags für die Nahfeldkommunikation, Karten und Readern"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"Displaysperre deaktivieren"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ermöglicht der App, die Tastensperre sowie den damit verbundenen Passwortschutz zu deaktivieren. Das Telefon deaktiviert die Tastensperre beispielsweise, wenn ein Anruf eingeht, und aktiviert sie wieder, nachdem das Gespräch beendet wurde."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Komplexität der Displaysperre erfahren und anfragen"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ermöglicht es der App, die Komplexitätsstufe der Displaysperre (hoch, mittel, niedrig oder keine) zu erfahren, was auf die mögliche Dauer und Art der Displaysperre hinweist. Die App kann Nutzern auch vorschlagen, die Displaysperre auf eine bestimmte Stufe zu aktualisieren, Nutzer können diesen Vorschlag jedoch auch einfach ignorieren und fortfahren. Beachten Sie, dass die Displaysperre nicht im Klartext gespeichert ist, sodass die App nicht das genaue Passwort kennt."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Biometrische Hardware verwenden"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Erlaubt der App, biometrische Hardware zur Authentifizierung zu verwenden"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"Fingerabdruckhardware verwalten"</string>
@@ -1083,21 +1087,21 @@
<string name="deleteText" msgid="6979668428458199034">"Löschen"</string>
<string name="inputMethod" msgid="1653630062304567879">"Eingabemethode"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Textaktionen"</string>
- <string name="email" msgid="4560673117055050403">"E-Mail"</string>
+ <string name="email" msgid="4560673117055050403">"E-Mail senden"</string>
<string name="email_desc" msgid="3638665569546416795">"E-Mail an ausgewählte Adresse senden"</string>
<string name="dial" msgid="1253998302767701559">"Anrufen"</string>
<string name="dial_desc" msgid="6573723404985517250">"Ausgewählte Telefonnummer anrufen"</string>
- <string name="map" msgid="5441053548030107189">"Karte"</string>
+ <string name="map" msgid="5441053548030107189">"Karte öffnen"</string>
<string name="map_desc" msgid="1836995341943772348">"Ausgewählte Adresse finden"</string>
<string name="browse" msgid="1245903488306147205">"Öffnen"</string>
<string name="browse_desc" msgid="8220976549618935044">"Ausgewählte URL öffnen"</string>
- <string name="sms" msgid="4560537514610063430">"SMS"</string>
+ <string name="sms" msgid="4560537514610063430">"SMS senden"</string>
<string name="sms_desc" msgid="7526588350969638809">"SMS an ausgewählte Telefonnummer senden"</string>
<string name="add_contact" msgid="7867066569670597203">"Hinzufügen"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Zu Kontakten hinzufügen"</string>
- <string name="view_calendar" msgid="979609872939597838">"Anzeigen"</string>
+ <string name="view_calendar" msgid="979609872939597838">"Aufrufen"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Ausgewählte Zeit im Kalender anzeigen"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Terminübersicht"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Termin planen"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Termin für die ausgewählte Zeit planen"</string>
<string name="view_flight" msgid="7691640491425680214">"Verfolgen"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Ausgewählten Flug verfolgen"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet wird aktualisiert…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Gerät wird aktualisiert…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Smartphone wird gestartet…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android wird gestartet…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet wird gestartet…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Gerät wird gestartet…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Speicher wird optimiert"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tippen, um alle Netzwerke zu sehen"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Verbinden"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle Netzwerke"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Mit einem von <xliff:g id="NAME">%s</xliff:g> vorgeschlagenen WLAN-Netzwerk verbunden"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Möchten Sie, dass <xliff:g id="NAME">%s</xliff:g> Ihnen Netzwerke vorschlägt?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nein"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WLAN wird automatisch aktiviert"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wenn du in der Nähe eines sicheren gespeicherten Netzwerks bist"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nicht wieder aktivieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 3c251e2..f899309 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Επιτρέπει στην εφαρμογή την πρόσβαση στις λειτουργίες τηλεφώνου της συσκευής. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να καθορίζει τον αριθμό τηλεφώνου και τα αναγνωριστικά συσκευών, εάν μια κλήση είναι ενεργή, καθώς και τον απομακρυσμένο αριθμό που συνδέεται από μια κλήση."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"δρομολόγηση κλήσεων μέσω του συστήματος"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Επιτρέπει στην εφαρμογή να δρομολογεί τις κλήσεις της μέσω του συστήματος για να βελτιώσει την εμπειρία κλήσης."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"προβολή και έλεγχος κλήσεων μέσω του συστήματος."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Επιτρέπει στην εφαρμογή να βλέπει και να ελέγχει τις εισερχόμενες κλήσεις στη συσκευή. Αυτό περιλαμβάνει πληροφορίες όπως τους αριθμούς κλήσεων για τις κλήσεις και την κατάσταση των κλήσεων."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"συνέχιση κλήσης από άλλη συσκευή"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Επιτρέπει στην εφαρμογή να συνεχίσει μια κλήση η οποία ξεκίνησε σε άλλη εφαρμογή."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ανάγνωση αριθμών τηλεφώνου"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Επιτρέπει στην εφαρμογή την επικοινωνία με ετικέτες, κάρτες και αναγνώστες της Επικοινωνίας κοντινού πεδίου (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"απενεργοποιεί το κλείδωμα οθόνης"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Επιτρέπει στην εφαρμογή την απενεργοποίηση του κλειδώματος πληκτρολογίου και άλλης σχετικής ασφάλειας με κωδικό πρόσβασης. Για παράδειγμα, το κλείδωμα πληκτρολογίου στο τηλέφωνο απενεργοποιείται όταν λαμβάνεται εισερχόμενη τηλεφωνική κλήση και ενεργοποιείται ξανά όταν η κλήση τερματιστεί."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"λήψη και υποβολή αιτήματος για πολυπλοκότητα οθόνης κλειδώματος"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Επιτρέπει στην εφαρμογή να μάθει το επίπεδο πολυπλοκότητας του κλειδώματος οθόνης (υψηλό, μέσο, χαμηλό ή κανένα), το οποίο υποδεικνύει το πιθανό εύρος του μήκους και του τύπου κλειδώματος οθόνης. Η εφαρμογή μπορεί επίσης να προτείνει στους χρήστες να ενημερώσουν το κλείδωμα οθόνης σε ένα συγκεκριμένο επίπεδο, όμως οι χρήστες μπορούν να την αγνοήσουν και να συνεχίσουν. Λάβετε υπόψη ότι το κλείδωμα οθόνης δεν αποθηκεύεται σε απλό κείμενο. Συνεπώς, η εφαρμογή δεν γνωρίζει τον κωδικό."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"χρήση βιομετρικού εξοπλισμού"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί βιομετρικό εξοπλισμό για έλεγχο ταυτότητας"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"διαχειρίζεται τον εξοπλισμό δακτυλικού αποτυπώματος"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Ενημέρωση tablet…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Ενημέρωση συστήματος…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Εκκίνηση τηλεφώνου…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Εκκίνηση Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Εκκίνηση tablet…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Εκκίνηση συσκευής…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Βελτιστοποίηση αποθηκευτικού χώρου."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Πατήστε για να δείτε όλα τα δίκτυα"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Σύνδεση"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Όλα τα δίκτυα"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Σύνδεση στο δίκτυο Wi‑Fi που προτάθηκε από την εφαρμογή <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Θέλετε να επιτρέψετε στην εφαρμογή <xliff:g id="NAME">%s</xliff:g> να σας προτείνει δίκτυα;"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ναι"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Όχι"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Το Wi‑Fi θα ενεργοποιηθεί αυτόματα"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Όταν βρίσκεστε κοντά σε αποθηκευμένο δίκτυο υψηλής ποιότητας"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Να μην ενεργοποιηθεί ξανά"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 4b983c0..7288a80 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 821ac1e..0481755 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4b983c0..7288a80 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 4b983c0..7288a80 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 3781945..5d3b466 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active, and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"see and control calls through the system."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Allows the app to see and control ongoing calls on the device. This includes information such as call numbers for calls and the state of the calls."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Allows the app to communicate with Near Field Communication (NFC) tags, cards, and readers."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"disable your screen lock"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"get and request screen lock complexity"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plaintext so the app does not know the exact password."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"manage fingerprint hardware"</string>
@@ -1179,6 +1183,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet is updating…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Device is updating…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Phone is starting…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android is starting…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet is starting…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Device is starting…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizing storage."</string>
@@ -1237,6 +1242,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connected to Wi‑Fi network proposed by <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Do you want to let <xliff:g id="NAME">%s</xliff:g> propose networks for you?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 50ebc49..d369da5 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"Transmite llamadas a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que la app transmita las llamadas a través del sistema para mejorar la experiencia de llamadas."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Mirar y controlar las llamadas con el sistema"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que la app vea y controle las llamadas entrantes del dispositivo. Incluye información como los números emisores y el estado de las llamadas."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar llamada de otra app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que la app continúe con una llamada que se inició en otra app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"leer números de teléfono"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permite que la aplicación se comunique con lectores, tarjetas y etiquetas de Comunicación de campo cercano (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"desactivar el bloqueo de pantalla"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que la aplicación desactive el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el dispositivo puede desactivar el bloqueo del teclado cuando recibe una llamada telefónica y volver a activarlo cuando finaliza la llamada."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtener y solicitar complejidad del bloqueo de pantalla"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que la app conozca el nivel de complejidad del bloqueo de pantalla (alta, media, baja o ninguna), lo que indica el rango de duración posible y el tipo de bloqueo de pantalla. La app también puede sugerirles a los usuarios que actualicen el bloqueo de pantalla a un determinado nivel, aunque ellos pueden ignorar esta sugerencia y seguir navegando. Ten en cuenta que el bloqueo de pantalla no se almacena como texto sin formato, por lo que la app no conoce la contraseña exacta."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"usar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que la app use hardware biométrico para realizar la autenticación"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"Administrar el hardware de huellas digitales"</string>
@@ -1088,7 +1092,7 @@
<string name="dial" msgid="1253998302767701559">"Llamar"</string>
<string name="dial_desc" msgid="6573723404985517250">"Llamar al número de teléfono seleccionado"</string>
<string name="map" msgid="5441053548030107189">"Mapa"</string>
- <string name="map_desc" msgid="1836995341943772348">"Abrir la dirección seleccionada en el mapa"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Buscar la dirección seleccionada"</string>
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
<string name="browse_desc" msgid="8220976549618935044">"Abrir URL seleccionada"</string>
<string name="sms" msgid="4560537514610063430">"Mensaje"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Actualizando tablet…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Actualizando dispositivo…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Iniciando teléfono…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Iniciando Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Iniciando tablet…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Iniciando dispositivo…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Presiona para ver todas las redes"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectar"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas las redes"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Se estableció conexión con la red Wi-Fi sugerida por <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"¿Quieres permitir que <xliff:g id="NAME">%s</xliff:g> te sugiera redes?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sí"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Se activará la conexión Wi-Fi automáticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Cuando estés cerca de una red guardada de alta calidad"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"No volver a activar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7a992b4..9174248 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"direccionar llamadas a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite a la aplicación direccionar sus llamadas hacia el sistema para mejorar la calidad de estas."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver y controlar llamadas a través del sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que la aplicación vea y controle las llamadas entrantes en el dispositivo. Esto incluye información como los números de las llamadas y su estado."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar una llamada de otra aplicación"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que la aplicación continúe una llamada que se ha iniciado en otra aplicación."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"leer números de teléfono"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permite que la aplicación se comunique con lectores, tarjetas y etiquetas de Comunicación de campo cercano (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"inhabilitar el bloqueo de pantalla"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que la aplicación inhabilite el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el teléfono puede inhabilitar el bloqueo del teclado cuando se recibe una llamada telefónica y volver a habilitarlo cuando finaliza la llamada."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtener y solicitar el nivel de complejidad del bloqueo de pantalla"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que la aplicación entienda el nivel de complejidad del bloqueo de pantalla (alto, medio, bajo o ninguno) que indica la longitud y el tipo del bloqueo de pantalla posibles. La aplicación también puede sugerir a los usuarios que actualicen el bloqueo de pantalla para que tenga un nivel concreto de complejidad, pero los usuarios pueden ignorar la advertencia libremente. El bloqueo de pantalla no se almacena en texto sin formato, así que la aplicación no puede saber cuál es la contraseña exacta."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"usar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que la aplicación utilice el hardware biométrico para realizar la autenticación"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrar hardware de huellas digitales"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"Eliminar"</string>
<string name="inputMethod" msgid="1653630062304567879">"Método de introducción de texto"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
- <string name="email" msgid="4560673117055050403">"Correo electrónico"</string>
+ <string name="email" msgid="4560673117055050403">"Enviar correo"</string>
<string name="email_desc" msgid="3638665569546416795">"Enviar un correo electrónico a la dirección seleccionada"</string>
<string name="dial" msgid="1253998302767701559">"Llamar"</string>
<string name="dial_desc" msgid="6573723404985517250">"Llamar al número de teléfono seleccionado"</string>
@@ -1091,15 +1095,15 @@
<string name="map_desc" msgid="1836995341943772348">"Buscar la dirección seleccionada"</string>
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
<string name="browse_desc" msgid="8220976549618935044">"Abrir la URL seleccionada"</string>
- <string name="sms" msgid="4560537514610063430">"Mensaje"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Enviar un mensaje al número de teléfono seleccionado"</string>
+ <string name="sms" msgid="4560537514610063430">"Enviar SMS"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Enviar SMS al teléfono seleccionado"</string>
<string name="add_contact" msgid="7867066569670597203">"Añadir"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Añadir a contactos"</string>
<string name="view_calendar" msgid="979609872939597838">"Ver"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Ver la hora seleccionada en el calendario"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Programar"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Programar un evento para la hora seleccionada"</string>
- <string name="view_flight" msgid="7691640491425680214">"Buscar"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Hacer seguimiento"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Seguir el vuelo seleccionado"</string>
<string name="translate" msgid="9218619809342576858">"Traducir"</string>
<string name="translate_desc" msgid="4502367770068777202">"Traducir el texto seleccionado"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"El tablet se está actualizando…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"El dispositivo se está actualizando…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"El teléfono se está iniciando…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se está iniciando…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"El tablet se está iniciando…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"El dispositivo se está iniciando…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toca para ver todas las redes"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectarse"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas las redes"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conexión establecida con la red Wi‑Fi recomendada por <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"¿Quieres que <xliff:g id="NAME">%s</xliff:g> te recomiende redes?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sí"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"La conexión Wi‑Fi se activará automáticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Cuando estés cerca de una red de alta calidad guardada"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"No volver a activar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index e633ab4..f20b35f 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Annab rakendusele juurdepääsu seadme telefonifunktsioonidele. See luba võimaldab rakendusel määrata telefoninumbri ja seadme ID-d ning kontrollida, kas kõne on aktiivne ja kaugnumber on kõne kaudu telefoniga ühendatud."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"kõnede marsruutimine süsteemi kaudu"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Võimaldab rakendusel kõnesid süsteemi kaudu marsruutida, et helistamiskogemust täiustada."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"süsteemi kaudu kõnede vaatamine ja juhtimine."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Lubab rakendusel seadmes vaadata ja juhtida käimasolevaid kõnesid. See hõlmab sellist teavet nagu kõnede numbrid ja olek."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"jätka kõnet teises rakenduses"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Lubab rakendusel jätkata kõnet, mida alustati teises rakenduses."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lugeda telefoninumbreid"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Võimaldab rakendusel suhelda lähiväljaside (NFC) märgendite, kaartide ja lugeritega."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"keelake ekraanilukk"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Võimaldab rakendusel keelata klahviluku ja muu seotud parooli turvalisuse. Näiteks keelab telefon klahviluku sissetuleva kõne vastuvõtmisel ja lubab klahviluku uuesti, kui kõne on lõpetatud."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Hankida ja taotleda ekraaniluku keerukuse teavet"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lubab rakendusel vaadata ekraaniluku keerukuse taset (kõrge, keskmine, madal või puudub), mis näitab ekraaniluku võimalikku pikkust ja tüüpi. Rakendus võib kasutajatele soovitada ka ekraaniluku viimist teatud tasemele, kuid kasutajad võivad seda eirata ja kuvalt lahkuda. Pange tähele, et ekraanilukku ei salvestata lihttekstina, seega ei tea rakendus täpset parooli."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"kasutada biomeetrilist riistvara"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Võimaldab rakendusel autentimiseks kasutada biomeetrilist riistvara"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"sõrmejälje riistvara haldamine"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tahvelarvutit värskendatakse …"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Seadet värskendatakse …"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon käivitub …"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android käivitub ..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tahvelarvuti käivitub …"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Seade käivitub …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Salvestusruumi optimeerimine."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Puudutage kõikide võrkude nägemiseks"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Ühenda"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Kõik võrgud"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Ühendatud rakenduse <xliff:g id="NAME">%s</xliff:g> soovitatud WiFi-võrguga"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Kas soovite lubada rakendusel <xliff:g id="NAME">%s</xliff:g> teile võrke soovitada?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Jah"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ei"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WiFi lülitub sisse automaatselt"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kui olete kvaliteetse salvestatud võrgu läheduses"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ära lülita tagasi sisse"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index ea9414f..153eeae 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Gailuaren telefono-eginbideak atzitzeko baimena ematen die aplikazioei. Baimen horrek aplikazioari telefono-zenbakia eta gailu IDak zein diren, deirik aktibo dagoen eta deia zer zenbakirekin konektatuta dagoen zehazteko baimena ematen die aplikazioei."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"bideratu deiak sistemaren bidez"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Deiak sistemaren bidez bideratzea baimentzen die aplikazioei, deien zerbitzua ahal bezain ona izan dadin."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ikusi eta kontrolatu deiak sistemaren bidez."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Gailuan abian diren deiak eta deion informazioa ikusi eta kontrolatzeko baimena ematen dio aplikazioari; besteak beste, deien zenbakiak eta deien egoera."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"Jarraitu beste aplikazio batean hasitako deia"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Beste aplikazio batean hasitako dei bat jarraitzea baimentzen dio aplikazioari."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"irakurri telefono-zenbakiak"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Near Field Communication (NFC) etiketekin, txartelekin eta irakurgailuekin komunikatzea baimentzen die aplikazioei."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"desgaitu pantailaren blokeoa"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Teklen blokeoa eta erlazionatutako pasahitz-segurtasuna desgaitzeko baimena ematen die aplikazioei. Adibidez, telefonoak teklen blokeoa desgaitzen du telefono-deiak jasotzen dituenean, eta berriro gaitzen du deiak amaitzean."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"lortu eta eskatu pantailaren blokeoaren konplexutasuna"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Pantailaren blokeoaren konplexutasun-maila (handia, ertaina, txikia edo bat ere ez) ezagutzea baimentzen dio aplikazioari; horren bidez, pantailaren blokeoaren luzeraren barruti edo mota posiblea adierazten da. Halaber, erabiltzaileei pantailaren blokeoa maila jakin batera igotzeko iradoki diezaieke aplikazioak, baina erabiltzaileek horri ez ikusi egiteko eta aplikazioa erabiltzen jarraitzeko aukera dute. Kontuan izan pantailaren blokeoa ez dela gordetzen testu arrunt gisa; beraz, aplikazioak ez du jakingo pasahitza zehazki zein den."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Erabili hardware biometrikoa"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Autentifikatzeko hardware biometrikoa erabiltzea baimentzen die aplikazioei."</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"kudeatu erreferentzia-gako digitalen hardwarea"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"Ezabatu"</string>
<string name="inputMethod" msgid="1653630062304567879">"Idazketa-metodoa"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Testu-ekintzak"</string>
- <string name="email" msgid="4560673117055050403">"Posta"</string>
+ <string name="email" msgid="4560673117055050403">"Bidali mezu bat"</string>
<string name="email_desc" msgid="3638665569546416795">"Bidali mezu elektroniko bat hautatutako helbidera"</string>
<string name="dial" msgid="1253998302767701559">"Deitu"</string>
<string name="dial_desc" msgid="6573723404985517250">"Deitu hautatutako telefono-zenbakira"</string>
@@ -1091,7 +1095,7 @@
<string name="map_desc" msgid="1836995341943772348">"Bilatu hautatutako helbidea"</string>
<string name="browse" msgid="1245903488306147205">"Ireki"</string>
<string name="browse_desc" msgid="8220976549618935044">"Ireki hautatutako URLa"</string>
- <string name="sms" msgid="4560537514610063430">"Bidali mezua"</string>
+ <string name="sms" msgid="4560537514610063430">"Bidali SMS bat"</string>
<string name="sms_desc" msgid="7526588350969638809">"Bidali testu-mezu bat hautatutako telefono-zenbakira"</string>
<string name="add_contact" msgid="7867066569670597203">"Gehitu"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Gehitu kontaktuetan"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tableta eguneratzen ari da…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Gailua eguneratzen ari da…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonoa abiarazten ari da…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android abiarazten ari da…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tableta abiarazten ari da…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Gailua abiarazten ari da…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Memoria optimizatzen."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Sakatu hau sare guztiak ikusteko"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Konektatu"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sare guztiak"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> aplikazioak iradokitako Wi‑Fi sarera konektatuta"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> aplikazioak sareak iradoki diezazkizun nahi duzu?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Bai"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ez"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi konexioa automatikoki aktibatuko da"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Gordeta daukazun kalitate handiko sare batetik gertu zaudenean"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ez aktibatu berriro"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1099169..76845c8 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"به برنامه اجازه میدهد به ویژگیهای تلفن دستگاه شما دسترسی پیدا کند. این مجوز به برنامه اجازه میدهد شماره تلفن و شناسههای دستگاه، فعال بودن یک تماس و شماره راه دوری که با یک تماس متصل شده است را مشخص کند."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"برقرار کردن تماسها ازطریق سیستم"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"به برنامه امکان میدهد برای بهبود تجربه تماس، تماسهایش را ازطریق سیستم برقرار کند."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"دیدن و کنترل تماسها ازطریق سیستم."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"به برنامهها اجازه میدهد تماسهای درحال انجام را در این دستگاه ببیند و کنترل کند. این مورد شامل اطلاعاتی مانند شماره تلفن برای تماسها و وضعیت تماسها است."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ادامه دادن تماس از برنامهای دیگر"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"به برنامه اجازه میدهد تماسی را که در برنامه دیگری شروع شده ادامه دهد."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"خواندن شماره تلفنها"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"به برنامه اجازه میدهد تا با تگهای ارتباط میدان نزدیک (NFC)، کارتها و فایل خوان ارتباط برقرار کند."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"غیرفعال کردن قفل صفحه شما"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. بهعنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"دریافت و درخواست پیچیدگی قفل صفحه"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"به برنامه اجازه میدهد سطح پیچیدگی قفل صفحه (بالا، متوسط، پایین یا هیچکدام) را بیاموزد که نشانگر اشکال مختلف طول و نوع قفل صفحه است. همچنین برنامه میتواند به کاربران پیشنهاد دهد قفل صفحه را به سطح خاصی بهروزرسانی کنند، اما کاربران میتوانند آزادانه این پیشنهاد را نادیده بگیرند و به سطح دیگری بروند. توجه داشته باشید که قفل صفحه در قالب نوشتار ساده ذخیره نمیشود، بنابراین برنامه گذرواژه دقیق را نمیداند."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"استفاده از سختافزار بیومتریک"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"به برنامه امکان میدهد از سختافزار بیومتریک برای احراز هویت استفاده کند"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"مدیریت سختافزار اثر انگشت"</string>
@@ -1083,27 +1087,27 @@
<string name="deleteText" msgid="6979668428458199034">"حذف"</string>
<string name="inputMethod" msgid="1653630062304567879">"روش ورودی"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"کنشهای متنی"</string>
- <string name="email" msgid="4560673117055050403">"ایمیل"</string>
+ <string name="email" msgid="4560673117055050403">"فرستادن ایمیل"</string>
<string name="email_desc" msgid="3638665569546416795">"ارسال ایمیل به نشانی انتخابی"</string>
- <string name="dial" msgid="1253998302767701559">"تماس"</string>
+ <string name="dial" msgid="1253998302767701559">"تماس گرفتن"</string>
<string name="dial_desc" msgid="6573723404985517250">"تماس با شماره تلفن انتخابی"</string>
<string name="map" msgid="5441053548030107189">"نقشه"</string>
<string name="map_desc" msgid="1836995341943772348">"مکانیابی نشانی انتخابشده"</string>
<string name="browse" msgid="1245903488306147205">"باز کردن"</string>
<string name="browse_desc" msgid="8220976549618935044">"باز کردن نشانی وب انتخابی"</string>
- <string name="sms" msgid="4560537514610063430">"پیام"</string>
+ <string name="sms" msgid="4560537514610063430">"فرستادن پیام"</string>
<string name="sms_desc" msgid="7526588350969638809">"ارسال پیام به شماره تلفن انتخابی"</string>
<string name="add_contact" msgid="7867066569670597203">"افزودن"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"افزودن به مخاطبین"</string>
<string name="view_calendar" msgid="979609872939597838">"مشاهده"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"مشاهده زمان انتخابی در تقویم"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"زمانبندی"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"زمانبندی کردن"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"زمانبندی رویداد برای زمان انتخابی"</string>
- <string name="view_flight" msgid="7691640491425680214">"پیگیری"</string>
+ <string name="view_flight" msgid="7691640491425680214">"انجام پیگیری"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ردیابی پرواز انتخابی"</string>
<string name="translate" msgid="9218619809342576858">"ترجمه کردن"</string>
<string name="translate_desc" msgid="4502367770068777202">"ترجمه کردن نوشتار انتخابشده"</string>
- <string name="define" msgid="7394820043869954211">"تعریف"</string>
+ <string name="define" msgid="7394820043869954211">"تعریف کردن"</string>
<string name="define_desc" msgid="7910883642444919726">"تعریف نوشتار انتخابی"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"حافظه درحال پر شدن است"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"رایانه لوحی درحال بهروزرسانی است…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"دستگاه درحال بهروزرسانی است…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"تلفن درحال راهاندازی است…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android در حال راهاندازی است..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"رایانه لوحی درحال راهاندازی است…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"دستگاه درحال راهاندازی است…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"بهینهسازی فضای ذخیرهسازی."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"برای دیدن همه شبکهها ضربه بزنید"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"اتصال"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"همه شبکهها"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"متصل به شبکه Wi‑Fi پیشنهادی <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"میخواهید به <xliff:g id="NAME">%s</xliff:g> اجازه دهید شبکههایی را که به شما پیشنهاد کند؟"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"بله"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"خیر"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi بهطور خودکار روشن خواهد شد"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"وقتی نزدیک شبکه ذخیرهشده با کیفیت بالا هستید"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"دوباره روشن نشود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3f8f0d1..16c96cc 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Antaa sovelluksen käyttää laitteen puhelinominaisuuksia. Sovellus voi määrittää puhelinnumeron ja laitteen tunnuksen, puhelun tilan sekä soitetun numeron."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ohjata puhelut järjestelmän kautta"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tämä sallii sovelluksen ohjata puhelut järjestelmän kautta, mikä auttaa parantamaan puhelujen laatua."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"nähdä puhelut ja päättää niistä järjestelmässä"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Sovellus voi nähdä laitteella käynnissä olevat puhelut ja päättää niistä. Se näkee esimerkiksi puheluihin liittyvät numerot ja niiden tilat."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"jatkaa toisen sovelluksen puhelua"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Antaa sovelluksen jatkaa puhelua, joka aloitettiin toisessa sovelluksessa."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lukea puhelinnumeroita"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Antaa sovelluksen kommunikoida NFC (Near Field Communication) -tagien, -korttien ja -lukijoiden kanssa."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"poista näytön lukitus käytöstä"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Antaa sovelluksen ottaa näppäinlukon ja siihen liittyvän salasanasuojauksen pois käytöstä. Esimerkki: puhelin poistaa näppäinlukon käytöstä puhelun saapuessa ja asettaa lukon takaisin käyttöön puhelun päättyessä."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"pyytää näytön lukituksen monimutkaisuutta ja nähdä sen"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Sovellus saa luvan selvittää näytön lukituksen monimutkaisuuden (korkea, keskitaso tai matala) eli sen pituusluokan ja tyypin. Sovellus voi myös ehdottaa käyttäjille näytön lukituksen vaihtamista tietylle tasolle, mutta he voivat ohittaa tämän ja poistua. Itse näytön lukitusta ei säilytetä tekstimuodossa, joten sovellus ei tiedä sitä tarkalleen."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"käytä biometristä laitteistoa"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Sallii sovelluksen käyttää biometristä laitteistoa todennukseen"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"sormenjälkilaitteiston hallinnointi"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablettia päivitetään…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Laitetta päivitetään…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Puhelin käynnistyy…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android käynnistyy…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tabletti käynnistyy…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Laite käynnistyy…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimoidaan tallennustilaa."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Napauta, niin näet kaikki verkot."</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Yhdistä"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Kaikki verkot"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Yhdistetty ehdotettuun (<xliff:g id="NAME">%s</xliff:g>) Wi-Fi-verkkoon"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Haluatko, että <xliff:g id="NAME">%s</xliff:g> voi ehdottaa sinulle verkkoja?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Kyllä"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ei"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi käynnistyy automaattisesti"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kun olet lähellä laadukasta tallennettua verkkoa"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Älä käynnistä uudelleen"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 2bac413..6fb1f12 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"acheminer les appels dans le système"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permet à l\'application d\'acheminer ses appels dans le système afin d\'améliorer l\'expérience d\'appel."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"afficher et gérer les appels à l\'aide du système."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Autorise l\'application à afficher et à gérer les appels sortants sur l\'appareil. Cela comprend de l\'information comme les numéros pour les appels et l\'état des appels."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuer un appel d\'une autre application"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permet à l\'application de continuer un appel commencé dans une autre application."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lire les numéros de téléphone"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permet à l\'application de communiquer avec des bornes, des cartes et des lecteurs compatibles avec la technologie NFC (communication en champ proche)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"désactiver le verrouillage de l\'écran"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permet à l\'application de désactiver le verrouillage des touches et toute mesure de sécurité par mot de passe associée. Par exemple, votre téléphone désactive le verrouillage des touches lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtenir et demander la complexité de l\'écran de verrouillage"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Autorise l\'application à apprendre le niveau de complexité de l\'écran de verrouillage (élevé, moyen, faible ou aucun), qui indique la gamme possible de longueur et de type de verrouillage d\'écran. L\'application peut aussi suggérer aux utilisateurs de mettre à jour l\'écran de verrouillage afin d\'utiliser un certain niveau de complexité, mais ils peuvent ignorer la suggestion. Notez que le verrouillage d\'écran n\'est pas stocké en texte brut pour de manière à ce que l\'application n\'ait pas accès au mot de passe exact."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"utiliser le matériel biométrique"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permet à l\'application d\'utiliser du matériel biométrique pour l\'authentification"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"gérer le matériel d\'empreinte digitale"</string>
@@ -1083,24 +1087,24 @@
<string name="deleteText" msgid="6979668428458199034">"Supprimer"</string>
<string name="inputMethod" msgid="1653630062304567879">"Mode de saisie"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
- <string name="email" msgid="4560673117055050403">"Courriel"</string>
+ <string name="email" msgid="4560673117055050403">"Envoyer un courriel"</string>
<string name="email_desc" msgid="3638665569546416795">"Envoyer un courriel à l\'adresse sélectionnée"</string>
- <string name="dial" msgid="1253998302767701559">"Appel"</string>
+ <string name="dial" msgid="1253998302767701559">"Appeler"</string>
<string name="dial_desc" msgid="6573723404985517250">"Téléphoner au numéro sélectionné"</string>
- <string name="map" msgid="5441053548030107189">"Carte"</string>
+ <string name="map" msgid="5441053548030107189">"Ouvrir une carte"</string>
<string name="map_desc" msgid="1836995341943772348">"Localiser l\'adresse sélectionnée"</string>
<string name="browse" msgid="1245903488306147205">"Ouvrir"</string>
<string name="browse_desc" msgid="8220976549618935044">"Ouvrir l\'adresse URL sélectionnée"</string>
- <string name="sms" msgid="4560537514610063430">"Message"</string>
+ <string name="sms" msgid="4560537514610063430">"Envoyer un texto"</string>
<string name="sms_desc" msgid="7526588350969638809">"Envoyer un message texte au numéro de téléphone sélectionné"</string>
<string name="add_contact" msgid="7867066569670597203">"Ajouter"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Ajouter aux contacts"</string>
<string name="view_calendar" msgid="979609872939597838">"Afficher"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Voir la date sélectionnée dans l\'agenda"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Calendrier"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifier un événement pour l\'heure sélectionnée"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Planifier"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifier un événement à l\'heure sélectionnée"</string>
<string name="view_flight" msgid="7691640491425680214">"Effectuer le suivi"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Faire le suivi du vol sélectionné"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Obtenir des informations sur le vol sélectionné"</string>
<string name="translate" msgid="9218619809342576858">"Traduire"</string>
<string name="translate_desc" msgid="4502367770068777202">"Traduire le texte sélectionné"</string>
<string name="define" msgid="7394820043869954211">"Définir"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Mise à jour de la tablette en cours…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Mise à jour de l\'appareil en cours…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Démarrage du téléphone en cours…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android en cours de démarrage..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Démarrage de la tablette en cours…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Démarrage de l\'appareil en cours…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Touchez pour afficher tous les réseaux"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connexion"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tous les réseaux"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connecté au réseau Wi-Fi proposé par <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Voulez-vous autoriser <xliff:g id="NAME">%s</xliff:g> à vous suggérer des réseaux?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Oui"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Non"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi s\'activera automatiquement"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes près d\'un réseau enregistré de haute qualité"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c0c2ed8..fa8c7f0 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"acheminer les appels via le système"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Autorise l\'application à acheminer les appels via le système afin d\'optimiser le confort d\'utilisation."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"voir et contrôler les appels via le système."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permet à l\'application de voir et contrôler les appels en cours sur l\'appareil. Cela inclut des informations telles que les numéros associés aux appels et l\'état des appels."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuer un appel issu d\'une autre application"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Autorise l\'application à continuer un appel qui a été démarré dans une autre application."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lire les numéros de téléphone"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permet à l\'application de communiquer avec des tags, des cartes et des lecteurs compatibles avec la technologie NFC (communication en champ proche)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"Désactiver le verrouillage de l\'écran"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permet à l\'application de désactiver le verrouillage des touches et toute mesure de sécurité via mot de passe associée. Par exemple, votre téléphone désactive le verrouillage des touches lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obtenir et demander la complexité du verrouillage de l\'écran"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permet à l\'application de connaître le niveau de complexité du verrouillage de l\'écran (élevé, moyen, faible ou aucun), qui indique les possibilités en matière de longueur du mot de passe et de type de verrouillage de l\'écran. L\'application peut également suggérer aux utilisateurs de modifier la complexité du verrouillage, mais ces derniers peuvent librement l\'ignorer. Remarque : Comme le verrouillage de l\'écran n\'est pas stocké au format texte brut, l\'application ne connaît pas le mot de passe exact."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"utiliser les composants biométriques"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Autoriser l\'application à utiliser les composants biométriques pour l\'authentification"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"Gérer le matériel d\'empreintes digitales"</string>
@@ -1083,16 +1087,16 @@
<string name="deleteText" msgid="6979668428458199034">"Supprimer"</string>
<string name="inputMethod" msgid="1653630062304567879">"Mode de saisie"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
- <string name="email" msgid="4560673117055050403">"E-mail"</string>
+ <string name="email" msgid="4560673117055050403">"Envoyer un e-mail"</string>
<string name="email_desc" msgid="3638665569546416795">"Envoyer un e-mail à l\'adresse sélectionnée"</string>
<string name="dial" msgid="1253998302767701559">"Appeler"</string>
<string name="dial_desc" msgid="6573723404985517250">"Appeler le numéro de téléphone sélectionné"</string>
- <string name="map" msgid="5441053548030107189">"Carte"</string>
+ <string name="map" msgid="5441053548030107189">"Ouvrir une carte"</string>
<string name="map_desc" msgid="1836995341943772348">"Localiser l\'adresse sélectionnée"</string>
<string name="browse" msgid="1245903488306147205">"Ouvrir"</string>
<string name="browse_desc" msgid="8220976549618935044">"Ouvrir l\'URL sélectionnée"</string>
- <string name="sms" msgid="4560537514610063430">"Envoyer un message"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Envoyer un message au numéro de téléphone sélectionné"</string>
+ <string name="sms" msgid="4560537514610063430">"Envoyer un SMS"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Envoyer un SMS au numéro de téléphone sélectionné"</string>
<string name="add_contact" msgid="7867066569670597203">"Ajouter"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Ajouter aux contacts"</string>
<string name="view_calendar" msgid="979609872939597838">"Afficher"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Mise à jour de la tablette…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Mise à jour de l\'appareil…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Démarrage du téléphone…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Démarrage d\'Android en cours"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Démarrage de la tablette…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Démarrage de l\'appareil…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage en cours…"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Appuyer pour afficher tous les réseaux"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Se connecter"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tous les réseaux"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Connecté au réseau Wi‑Fi proposé par <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Voulez-vous autoriser <xliff:g id="NAME">%s</xliff:g> à vous proposer des réseaux ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Oui"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Non"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi sera activé automatiquement"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes à proximité d\'un réseau enregistré de haute qualité"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index c5a4066..2c9b09d 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite á aplicación acceder ás funcións de teléfono do dispositivo. Con este permiso a aplicación pode determinar o número de teléfono e os ID do dispositivo, se unha chamada está activa e o número remoto conectado mediante unha chamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"dirixir as chamadas a través do sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite á aplicación dirixir as súas chamadas a través do sistema para mellorar a túa experiencia durante as chamadas."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"consultar e controlar as chamadas a través do sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que a aplicación consulte e controle as chamadas en curso do dispositivo. Pode acceder a información como os números e os estados das chamadas."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar unha chamada iniciada noutra aplicación"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que a aplicación continúe unha chamada que se iniciou noutra aplicación."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de teléfono"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permite á aplicación comunicarse con etiquetas, tarxetas e lectores Near Field Communication (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"desactivar o bloqueo da pantalla"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite á aplicación desactivar o bloqueo do teclado e calquera seguranza dos contrasinais asociada. Por exemplo, o teléfono desactiva o bloqueo do teclado ao recibir unha chamada telefónica entrante e, a continuación, volve activar o bloqueo do teclado unha vez finalizada a chamada."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"consultar complexidade do bloqueo de pantalla e suxerir aumentala"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que a aplicación consulte o nivel (alto, medio, baixo ou inexistente) de complexidade do bloqueo de pantalla para saber a lonxitude aproximada do contrasinal e o tipo de bloqueo de pantalla. A aplicación tamén pode suxerirlles aos usuarios que aumenten o nivel de complexidade do bloqueo de pantalla, aínda que poden ignorar a suxestión e seguir navegando. Ten en conta que o bloqueo de pantalla non se almacena como texto sen formato, polo que a aplicación non pode consultar o contrasinal exacto."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"utilizar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que a aplicación utilice hardware biométrico para a autenticación"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"xestionar hardware de impresión dixital"</string>
@@ -1091,7 +1095,7 @@
<string name="map_desc" msgid="1836995341943772348">"Localiza o enderezo seleccionado"</string>
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
<string name="browse_desc" msgid="8220976549618935044">"Abre o URL seleccionado"</string>
- <string name="sms" msgid="4560537514610063430">"Mensaxe"</string>
+ <string name="sms" msgid="4560537514610063430">"Enviar SMS"</string>
<string name="sms_desc" msgid="7526588350969638809">"Envía unha mensaxe ao número de teléfono seleccionado"</string>
<string name="add_contact" msgid="7867066569670597203">"Engadir"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Engade o elemento aos contactos"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"A tableta estase actualizando…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo estase actualizando…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"O teléfono estase iniciando…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Estase iniciando Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"A tableta estase iniciando…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo estase iniciando…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamento."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toca para ver todas as redes"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectarse"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectácheste á rede wifi proposta pola aplicación <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Queres permitir que a aplicación <xliff:g id="NAME">%s</xliff:g> che propoña redes?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Si"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Non"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"A wifi activarase automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Cando esteas preto dunha rede gardada de alta calidade"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Non volver activar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4908ed2..da8a6e5 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -322,7 +322,7 @@
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"સ્પર્શ કરીને શોધખોળ કરવું ચાલુ કરો"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"ટૅપ કરેલ આઇટમ મોટેથી બોલવામાં આવશે અને હાવભાવની મદદથી સ્ક્રીનને શોધી શકાય છે."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"તમે લખો તે ટેક્સ્ટનું અવલોકન કરો"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર્સ અને પાસવર્ડ્સ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર અને પાસવર્ડ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"પ્રદર્શન વિસ્તૃતિકરણ નિયંત્રિત કરો"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"પ્રદર્શનનું ઝૂમ સ્તર અને સ્થિતિનિર્ધારણ નિયંત્રિત કરો."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"હાવભાવ કરો"</string>
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"એપ્લિકેશનને ફોન સુવિધાઓને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને ફોન નંબર અને ઉપકરણ ID, કૉલ સક્રિય છે અને કોઈ કૉલ દ્વારા કનેક્ટ થયેલ રિમોટ નંબર નિર્ધારિત કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"સિસ્ટમ મારફતે કૉલ બીજે વાળો"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"કૉલિંગ અનુભવ સુધારવા માટે ઍપ્લિકેશનને સિસ્ટમ મારફતે કૉલ બીજે વાળવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"સિસ્ટમ મારફતે કૉલ જુઓ અને નિયંત્રિત કરો."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ઍપને ડિવાઇસ પરના ચાલી રહેલા કૉલને જોવાની અને નિયંત્રિત કરવાની મંજૂરી આપે છે. આમાં કૉલ માટેના કૉલ નંબર અને તેની સ્થિતિ જેવી માહિતી શામેલ હોય છે."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"તૃતીય પક્ષ ઍપમાંનો કૉલ ચાલુ રાખો"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"એક અન્ય તૃતીય પક્ષ ઍપમાં ચાલુ થયેલા કૉલને આ ઍપમાં ચાલુ રાખવાની મંજૂરી આપે છે."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ફોન નંબર વાંચો"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ઍપ્લિકેશનને નિઅર ફીલ્ડ કમ્યુનિકેશન (NFC) ટૅગ, કાર્ડ અને રીડર સાથે સંચાર કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"તમારું સ્ક્રીન લૉક અક્ષમ કરો"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"એપ્લિકેશનને કીલૉક અને કોઈપણ સંકળાયેલ પાસવર્ડ સુરક્ષા અક્ષમ કરવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે, ઇનકમિંગ ફોન કૉલ પ્રાપ્ત કરતી વખતે ફોન, કીલૉકને અક્ષમ કરે છે, પછી કૉલ સમાપ્ત થઈ જવા પર કીલૉક ફરીથી સક્ષમ કરે છે."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"સ્ક્રીન લૉકની જટિલતા મેળવીને વિનંતી કરો"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ઍપને સ્ક્રીન લૉકની જટિલતાનું લેવલ (ઊંચું, મધ્યમ, નીચું અથવા કોઈ નહીં) જાણવાની મંજૂરી આપે છે, જે સ્ક્રીન લૉકના પ્રકાર અને લંબાઈની સંભવિત શ્રેણી સૂચવે છે. ઍપ વપરાશકર્તાઓને સ્ક્રીન લૉકને ચોક્કસ લેવલ સુધી અપડેટ કરવાનું સૂચન પણ કરી શકે છે, પરંતુ વપરાશકર્તાઓ મુક્ત રીતે અવગણીને નૅવિગેટ કરી શકે છે. નોંધી લો કે સ્ક્રીન લૉકનો plaintextમાં સંગ્રહ કરવામાં આવતો નથી, તેથી ઍપને ચોક્કસ પાસવર્ડની જાણ હોતી નથી."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરો"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ઍપને પ્રમાણીકરણ માટે બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ફિંગરપ્રિન્ટ હાર્ડવેરને સંચાલિત કરો"</string>
@@ -1083,11 +1087,11 @@
<string name="deleteText" msgid="6979668428458199034">"ડિલીટ કરો"</string>
<string name="inputMethod" msgid="1653630062304567879">"ઇનપુટ પદ્ધતિ"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"ટેક્સ્ટ ક્રિયાઓ"</string>
- <string name="email" msgid="4560673117055050403">"ઇમેઇલ"</string>
+ <string name="email" msgid="4560673117055050403">"ઇમેઇલ કરો"</string>
<string name="email_desc" msgid="3638665569546416795">"પસંદ કરેલ ઍડ્રેસ પર ઇમેઇલ મોકલો"</string>
<string name="dial" msgid="1253998302767701559">"કૉલ કરો"</string>
<string name="dial_desc" msgid="6573723404985517250">"પસંદ કરેલ ફોન નંબર પર કૉલ કરો"</string>
- <string name="map" msgid="5441053548030107189">"નકશો"</string>
+ <string name="map" msgid="5441053548030107189">"નકશો ખોલો"</string>
<string name="map_desc" msgid="1836995341943772348">"પસંદ કરેલ સરનામું શોધો"</string>
<string name="browse" msgid="1245903488306147205">"ખોલો"</string>
<string name="browse_desc" msgid="8220976549618935044">"પસંદ કરેલ URL ખોલો"</string>
@@ -1096,7 +1100,7 @@
<string name="add_contact" msgid="7867066569670597203">"ઉમેરો"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"સંપર્કોમાં ઉમેરો"</string>
<string name="view_calendar" msgid="979609872939597838">"જુઓ"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"કૅલેન્ડરમાં પસંદ કરેલ સમય જુઓ"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"કૅલેન્ડરમાં પસંદ કરેલો સમય જુઓ"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"શેડ્યૂલ કરો"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"પસંદ કરેલ સમય માટે ઇવેન્ટ શેડ્યૂલ કરો"</string>
<string name="view_flight" msgid="7691640491425680214">"ટ્રૅક કરો"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ટૅબ્લેટ અપડેટ થઈ રહ્યું છે…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ઉપકરણ અપડેટ થઈ રહ્યું છે…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ફોન શરૂ થઈ રહ્યો છે…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android પ્રારંભ થઈ રહ્યું છે…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ટૅબ્લેટ શરૂ થઈ રહ્યું છે…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ઉપકરણ શરૂ થઈ રહ્યું છે…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"સંગ્રહ ઓપ્ટિમાઇઝ કરી રહ્યું છે."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"બધા નેટવર્ક જોવા ટૅપ કરો"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"કનેક્ટ કરો"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"બધા નેટવર્કો"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> દ્વારા પ્રસ્તાવિત વાઇ-ફાઇ નેટવર્ક સાથે કનેક્ટ કર્યું"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"શું તમે <xliff:g id="NAME">%s</xliff:g>ને તમારા માટે નેટવર્કનો પ્રસ્તાવ આપવા દેવા માગો છો?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"હા"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ના"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"વાઇ-ફાઇ આપમેળે ચાલુ થઈ જશે"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"જ્યારે તમે એક ઉચ્ચ ક્વૉલિટીવાળા સાચવેલ નેટવર્કની નજીક હોવ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"પાછું ચાલુ કરશો નહીં"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 87e1f97..4349c09 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ऐप्स को डिवाइस की फ़ोन सुविधाओं तक पहुंचने देता है. यह अनुमति ऐप्स को फ़ोन नंबर और डिवाइस आईडी, कॉल सक्रिय है या नहीं, और कॉल द्वारा कनेक्ट किया गया दूरस्थ नंबर निर्धारित करने देती है."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"सिस्टम के माध्यम से कॉल रूट करें"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कॉल करने के अनुभव को बेहतर बनाने के लिए ऐप्लिकेशन को सिस्टम के माध्यम से उसके कॉल रूट करने देती है."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"सिस्टम के ज़रिए कॉल देखना और नियंत्रित करना."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ऐप्लिकेशन को डिवाइस पर चल रहे कॉल देखने और नियंत्रित करने देती है. इसमें कॉल के नंबर और उनकी स्थिति से जुड़ी जानकारी शामिल है."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"दूसरे ऐप्लिकेशन से शुरू किया गया कॉल जारी रखें"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"इसके ज़रिए आप, किसी ऐप्लिकेशन में शुरू किया गया कॉल दूसरे ऐप्लिकेशन में जारी रख सकते हैं."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फ़ोन नंबर पढ़ना"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ऐप्स को नियर फ़ील्ड कम्यूनिकेशन (NFC) टैग, कार्ड, और रीडर के साथ संचार करने देता है."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"अपना स्क्रीन लॉक अक्षम करें"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा अक्षम करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल प्राप्त करते समय फ़ोन, कीलॉक को अक्षम कर देता है, फिर कॉल समाप्त होने पर कीलॉक को पुन: सक्षम कर देता है."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"स्क्रीन लॉक की कठिनाई का अनुरोध करना और उसे जानना"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ऐप्लिकेशन को स्क्रीन लॉक की कठिनाई का लेवल (बहुत ज़्यादा, मध्यम, कम या बिल्कुल नहीं) जानने देती है, जो स्क्रीन लॉक की लंबाई और उसके प्रकार की उस सीमा के बारे में संकेत देता है, जो शायद हो सकती है. ऐप्लिकेशन उपयोगकर्ताओं को यह सुझाव भी दे सकता है कि वे स्क्रीन लॉक को एक तय लेवल तक अपडेट करें. लेकिन उपयोगकर्ता इसे बेझिझक अनदेखा करके छोड़ सकते हैं. ध्यान दें कि स्क्रीन लॉक को सादे टेक्स्ट में सेव नहीं किया जाता है इसलिए ऐप्लिकेशन को सटीक पासवर्ड पता नहीं होता है."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमीट्रिक हार्डवेयर इस्तेमाल करने दें"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"पुष्टि के लिए, ऐप्लिकेशन को बायोमीट्रिक हार्डवेयर इस्तेमाल करने की मंज़ूरी दें"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"उंगली की छाप के लिए हार्डवेयर को प्रबंधित करें"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"टैबलेट अपडेट हो रहा है…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"डिवाइस अपडेट हो रहा है…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"फ़ोन चालू हो रहा है…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android प्रारंभ हो रहा है…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"टैबलेट चालू हो रहा है…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"डिवाइस चालू हो रहा है…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"मेमोरी ऑप्टिमाइज़ हो रही है."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"सभी नेटवर्क देखने के लिए यहां पर टैप करें"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"कनेक्ट करें"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"सभी नेटवर्क"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> के दिए हुए वाई-फ़ाई नेटवर्क से कनेक्ट है"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"क्या आप <xliff:g id="NAME">%s</xliff:g> को आपके लिए नेटवर्क ऑफ़र करने देना चाहते हैं?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"हां"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"नहीं"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाई-फ़ाई अपने आप चालू हो जाएगा"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"जब आप किसी अच्छी क्वालिटी वाले सेव किए गए नेटवर्क के पास हों"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"वापस चालू न करें"</string>
@@ -1407,7 +1416,7 @@
<string name="ime_action_go" msgid="8320845651737369027">"जाएं"</string>
<string name="ime_action_search" msgid="658110271822807811">"खोजें"</string>
<string name="ime_action_send" msgid="2316166556349314424">"भेजें"</string>
- <string name="ime_action_next" msgid="3138843904009813834">"आगे"</string>
+ <string name="ime_action_next" msgid="3138843904009813834">"आगे बढ़ें"</string>
<string name="ime_action_done" msgid="8971516117910934605">"हो गया"</string>
<string name="ime_action_previous" msgid="1443550039250105948">"पीछे जाएं"</string>
<string name="ime_action_default" msgid="2840921885558045721">"निष्पादित करें"</string>
@@ -1451,7 +1460,7 @@
<string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग अक्षम है"</string>
<string name="disable_tether_notification_message" msgid="2913366428516852495">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
<string name="back_button_label" msgid="2300470004503343439">"वापस जाएं"</string>
- <string name="next_button_label" msgid="1080555104677992408">"आगे"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"आगे बढ़ें"</string>
<string name="skip_button_label" msgid="1275362299471631819">"अभी नहीं"</string>
<string name="no_matches" msgid="8129421908915840737">"कोई मिलान नहीं"</string>
<string name="find_on_page" msgid="1946799233822820384">"पेज पर ढूंढें"</string>
@@ -1639,7 +1648,7 @@
<string name="accessibility_shortcut_spoken_feedback" msgid="6143872712930414829">"मौजूदा सुलभता सुविधा शुरू करने के लिए \'सुलभता शॉर्टकट\' का फिर से इस्तेमाल करें"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"सुलभता बटन पर टैप करते समय इस्तेमाल की जाने वाली सुविधा चुनें:"</string>
<string name="accessibility_button_instructional_text" msgid="6942300463612999993">"सुविधाओं में बदलाव करने के लिए, सुलभता बटन को दबाकर रखें."</string>
- <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"बड़ा करें"</string>
+ <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"बड़ा करना"</string>
<string name="user_switched" msgid="3768006783166984410">"मौजूदा उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="2871009331809089783">"<xliff:g id="NAME">%1$s</xliff:g> पर स्विच किया जा रहा है…"</string>
<string name="user_logging_out_message" msgid="8939524935808875155">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा प्रस्थान किया जा रहा है…"</string>
@@ -1879,7 +1888,7 @@
<string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"फ़ाइलें देखने के लिए टैप करें"</string>
<string name="pin_target" msgid="3052256031352291362">"पिन करें"</string>
<string name="unpin_target" msgid="3556545602439143442">"अनपिन करें"</string>
- <string name="app_info" msgid="6856026610594615344">"ऐप की जानकारी"</string>
+ <string name="app_info" msgid="6856026610594615344">"ऐप्लिकेशन की जानकारी"</string>
<string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ हो रहा है…"</string>
<string name="demo_restarting_message" msgid="952118052531642451">"डिवाइस पुन: रीसेट कर रहा है…"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index ff2afba..2747521 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -450,6 +450,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Aplikaciji omogućuje pristup telefonskim značajkama uređaja. Ta dozvola aplikaciji omogućuje utvrđivanje telefonskog broja i ID-ova uređaja, je li poziv aktivan te udaljeni broj koji je povezan pozivom."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"preusmjeravati pozive putem sustava"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Omogućuje aplikaciji da preusmjerava pozive putem sustava radi poboljšanja doživljaja."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"vidjeti i kontrolirati pozive putem sustava."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Omogućuje aplikaciji da vidi i kontrolira pozive koji su u tijeku na uređaju. To uključuje podatke kao što su brojevi i stanja poziva."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"nastaviti poziv iz neke druge aplikacije"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Omogućuje aplikaciji da nastavi poziv započet u nekoj drugoj aplikaciji."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čitati telefonske brojeve"</string>
@@ -510,6 +512,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Aplikaciji omogućuje komunikaciju s oznakama, karticama i čitačima komunikacije kratkog dometa (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"onemogućavanje zaključavanja zaslona"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Aplikaciji omogućuje onemogućavanje zaključavanja tipkovnice i svih pripadajućih sigurnosnih zaporki. Na primjer, telefon onemogućuje zaključavanje tipkovnice kod primanja dolaznog telefonskog poziva, nakon kojeg se zaključavanje tipkovnice ponovo omogućuje."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dohvatiti i zahtijevati složenost zaključavanja zaslona"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Omogućuje aplikaciji da sazna razinu složenosti zaključavanja zaslona (visoka, srednja, niska ili nijedna), što upućuje na mogući raspon duljine i vrstu zaključavanja zaslona. Aplikacija također korisnicima može predložiti da ažuriraju zaključavanje zaslona na određenu razinu, no korisnici to mogu slobodno zanemariti i nastaviti dalje. Napominjemo da se zaključavanje zaslona ne pohranjuje u običnom tekstu, pa aplikacija ne zna točnu zaporku."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"koristiti biometrijski hardver"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Aplikaciji omogućuje upotrebu biometrijskog hardvera radi autentifikacije"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje hardverom za čitanje otisaka prstiju"</string>
@@ -1103,26 +1107,26 @@
<string name="deleteText" msgid="6979668428458199034">"Izbriši"</string>
<string name="inputMethod" msgid="1653630062304567879">"Način unosa"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Radnje s tekstom"</string>
- <string name="email" msgid="4560673117055050403">"E-pošta"</string>
- <string name="email_desc" msgid="3638665569546416795">"Slanje e-poruke na odabranu adresu"</string>
- <string name="dial" msgid="1253998302767701559">"Poziv"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Pozivanje odabranog telefonskog broja"</string>
- <string name="map" msgid="5441053548030107189">"Karta"</string>
- <string name="map_desc" msgid="1836995341943772348">"Prikaz odabrane adrese na karti"</string>
+ <string name="email" msgid="4560673117055050403">"Pošalji e-poštu"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Pošalji e-poštu na odabranu adresu"</string>
+ <string name="dial" msgid="1253998302767701559">"Nazovi"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Nazovi odabrani telefonski broj"</string>
+ <string name="map" msgid="5441053548030107189">"Prikaži na karti"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Prikaži odabrane adrese na karti"</string>
<string name="browse" msgid="1245903488306147205">"Otvori"</string>
- <string name="browse_desc" msgid="8220976549618935044">"Otvaranje odabranog URL-a"</string>
- <string name="sms" msgid="4560537514610063430">"Poruka"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Slanje poruke na odabrani telefonski broj"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"Otvori odabrani URL"</string>
+ <string name="sms" msgid="4560537514610063430">"Pošalji poruku"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Pošalji poruku na odabrani telefonski broj"</string>
<string name="add_contact" msgid="7867066569670597203">"Dodaj"</string>
- <string name="add_contact_desc" msgid="4830217847004590345">"Dodavanje u kontakte"</string>
+ <string name="add_contact_desc" msgid="4830217847004590345">"Dodaj u kontakte"</string>
<string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaz odabranog vremena u kalendaru"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Raspored"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakazivanje događaja za određeno vrijeme"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Prikaži odabrano vrijeme u kalendaru"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Zakaži"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Zakaži događaj za određeno vrijeme"</string>
<string name="view_flight" msgid="7691640491425680214">"Prati"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Praćenje odabranog leta"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Prati odabrani let"</string>
<string name="translate" msgid="9218619809342576858">"Prevedi"</string>
- <string name="translate_desc" msgid="4502367770068777202">"Prevedite odabrani tekst"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Prevedi odabrani tekst"</string>
<string name="define" msgid="7394820043869954211">"Definiraj"</string>
<string name="define_desc" msgid="7910883642444919726">"Definiraj odabrani tekst"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
@@ -1201,6 +1205,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet se ažurira…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Uređaj se ažurira…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se pokreće…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Pokretanje Androida..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet se pokreće…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Uređaj se pokreće…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje pohrane."</string>
@@ -1261,6 +1266,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dodirnite za prikaz svih mreža"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Poveži"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Sve mreže"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Povezani ste s Wi‑Fi mrežom koju je predložila aplikacija <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Želite li dopustiti da vam aplikacija <xliff:g id="NAME">%s</xliff:g> predlaže mreže?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi će se uključiti automatski"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kada ste u blizini spremljene mreže visoke kvalitete"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Više ne uključuj"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 59d5123..10538f4 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen az eszköz telefonálási funkcióihoz. Az engedéllyel rendelkező alkalmazás meghatározhatja a telefonszámot és eszközazonosítókat, hogy egy hívás aktív-e, valamint híváskor a másik fél telefonszámát."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"rendszeren keresztüli hívásirányítás"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"A telefonálási élmény javítása érdekében lehetővé teszi az alkalmazás számára a rendszeren keresztüli hívásirányítást."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Hívások megtekintése és vezérlése a rendszeren keresztül"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Engedélyezi az alkalmazásnak az eszközön folyamatban lévő hívások megtekintését és vezérlését. Ebbe beletartoznak az olyan információk is, mint a hívásban részt vevő felek hívószáma és a hívások állapota."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"Másik alkalmazásból indított hívás folytatása"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Engedélyezi az alkalmazásnak, hogy folytassa a hívást, amelyet valamelyik másik alkalmazásban kezdtek meg."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefonszámok olvasása"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Lehetővé teszi az alkalmazás számára, hogy NFC (Near Field Communication - kis hatósugarú vezeték nélküli kommunikáció) technológiát használó címkékkel, kártyákkal és leolvasókkal kommunikáljon."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"képernyőzár kikapcsolása"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Lehetővé teszi az alkalmazás számára a billentyűzár és bármely kapcsolódó jelszavas védelem kikapcsolását. Például a telefon feloldja a billentyűzárat bejövő hívás esetén, majd újra bekapcsolja azt a hívás végeztével."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"a képernyőzár összetettségének lekérdezése és lekérése"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lehetővé teszi az alkalmazás számára, hogy megismerje a képernyőzár összetettségi szintjét (magas, közepes, alacsony vagy nincs), amely jelzi a képernyőzár lehetséges típusát és hosszát. Az alkalmazás ezenkívül javaslatot tehet a felhasználóknak a képernyőzár bizonyos szintre való frissítésére, de ezt a javaslatot a felhasználók figyelmen kívül hagyhatják. A képernyőzárat nem egyszerű szöveges formátumban tárolja a rendszer, ezért az alkalmazás nem fogja tudni a pontos jelszót."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"biometrikus hardver használata"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Lehetővé teszi az alkalmazás számára a biometrikus hardver hitelesítésre való használatát"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ujjlenyomat-olvasó hardver kezelése"</string>
@@ -1084,7 +1088,7 @@
<string name="inputMethod" msgid="1653630062304567879">"Beviteli mód"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Műveletek szöveggel"</string>
<string name="email" msgid="4560673117055050403">"E-mail"</string>
- <string name="email_desc" msgid="3638665569546416795">"Kiválasztott cím elküldése e-mailben"</string>
+ <string name="email_desc" msgid="3638665569546416795">"E-mail küldése a kiválasztott címre"</string>
<string name="dial" msgid="1253998302767701559">"Hívás"</string>
<string name="dial_desc" msgid="6573723404985517250">"Kiválasztott telefonszám hívása"</string>
<string name="map" msgid="5441053548030107189">"Térkép"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"A táblagép frissítése…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Az eszköz frissítése…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"A telefon indítása…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Az Android indítása…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"A táblagép indítása…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Az eszköz indítása…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tárhely-optimalizálás."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Koppintással megjelenítheti az összes hálózatot"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Kapcsolódás"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Összes hálózat"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Csatlakozva a(z) <xliff:g id="NAME">%s</xliff:g> által javasolt Wi-Fi-hálózathoz"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Szeretné, hogy a(z) <xliff:g id="NAME">%s</xliff:g> hálózatokat javasoljon Önnek?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Igen"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nem"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"A Wi-Fi automatikusan bekapcsol"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Amikor jó minőségű mentett hálózat közelében tartózkodik"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne kapcsolódjon vissza"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d019f0e..fbb5f4b 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Թույլ է տալիս հավելվածին օգտագործել սարքի հեռախոսային գործիքները: Այս թույլտվությունը հավելվածին հնարավորություն է տալիս որոշել հեռախոսահամարը և սարքի ID-ները, արդյոք զանգը ակտիվ է և միացված զանգի հեռակա հեռախոսահամարը:"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"զանգերն ուղարկել համակարգի միջոցով"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Հավելվածին թույլ է տալիս իր զանգերն ուղարկել համակարգի միջոցով՝ կապի որակը բարձրացնելու նպատակով։"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Զանգերի դիտում և վերահսկում համակարգի միջոցով"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Թույլ է տալիս հավելվածին տեսնել և վերահսկել ընթացիկ զանգերը սարքում: Սա ներառում է այնպիսի տեղեկություններ, ինչպիսիք են, օրինակ, հեռախոսահամարները և զանգերի վիճակը:"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"շարունակել զանգը այլ հավելվածի միջոցով"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Թույլ է տալիս հավելվածին շարունակել մեկ այլ հավելվածի միջոցով սկսած զանգը:"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"օգտագործել հեռախոսահամարները"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Թույլ է տալիս հավելվածին հաղորդակցվել Մոտ տարածությամբ հաղորդակցման (NFC) պիտակների, քարտերի և ընթերցիչների հետ:"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"անջատել ձեր էկրանի կողպեքը"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Թույլ է տալիս հավելվածին անջատել ստեղնաշարի կողպումը և ցանկացած կապված գաղտնաբառի պաշտպանվածությունը: Սրա ճիշտ օրինակն է, երբ հեռախոսը անջատում է ստեղնաշարի կողպումը մուտքային զանգ ստանալիս, հետո այն կրկին միացնում է, երբ զանգը ավարտվում է:"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"էկրանի կողպման բարդության մակարդակի մասին տեղեկությունների և այդ մակարդակի բարձրացման հետ կապված խորհուրդների ստացում"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Հավելվածին հասանելի կդառնան էկրանի կողպման բարդության մակարդակի մասին տեղեկությունները (բարձր, միջին, ցածր կամ ոչ մի), այդ թվում կողպման տեսակի և գաղտնաբառի երկարության մասին տվյալները: Բացի այդ, հավելվածը կկարողանա առաջարկել օգտատերերին բարձրացնել կողպման բարդության մակարդակը: Օգտատերերը կարող են անտեսել այդ առաջարկները: Նկատի ունեցեք, որ գաղտնաբառը չի պահվում բաց տեքստի տեսքով և հասանելի չէ հավելվածին:"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"կենսաչափական սարքի օգտագործում"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Հավելվածին թույլ է տալիս օգտագործել նույնականացման համար նախատեսված կենսաչափական սարքը"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"կառավարել մատնահետքերի գրանցման սարքը"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Պլանշետը թարմացվում է…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Սարքը թարմացվում է…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Հեռախոսը միանում է…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android-ը մեկնարկում է…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Պլանշետը միանում է…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Սարքը միանում է…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Պահեստի օպտիմալացում:"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Հպեք՝ բոլոր ցանցերը տեսնելու համար"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Միանալ"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Բոլոր ցանցերը"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Միացել է «<xliff:g id="NAME">%s</xliff:g>» հավելվածի գտած Wi‑Fi ցանցին:"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Թույլատրե՞լ <xliff:g id="NAME">%s</xliff:g> հավելվածին առաջարկել ձեզ ցանցեր:"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Այո"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ոչ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi-ն ավտոմատ կմիանա"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Լավ ազդանշանով պահված ցանցի տարածքում գտնվելիս"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Նորից չմիացնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index cc70724..3c6e347 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Memungkinkan aplikasi mengakses fitur telepon perangkat. Izin ini memungkinkan aplikasi menentukan nomor telepon dan ID perangkat, apakah suatu panggilan aktif atau tidak, dan nomor jarak jauh yang tersambung oleh sebuah panggilan."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"sambungkan panggilan telepon melalui sistem"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Mengizinkan aplikasi menyambungkan panggilan telepon melalui sistem untuk menyempurnakan pengalaman menelepon."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"melihat dan mengontrol panggilan melalui sistem."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Memungkinkan aplikasi melihat dan mengontrol panggilan telepon yang sedang berlangsung di perangkat. Ini mencakup informasi seperti nomor yang menelepon dan status panggilan telepon."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"lanjutkan panggilan dari aplikasi lain"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Mengizinkan aplikasi melanjutkan panggilan yang dimulai di aplikasi lain."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"membaca nomor telepon"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Mengizinkan apl berkomunikasi dengan tag, kartu, dan alat pembaca Komunikasi Nirkabel Jarak Dekat (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"nonaktifkan kunci layar Anda"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Memungkinkan aplikasi menonaktifkan kunci tombol dan keamanan sandi apa pun yang terkait. Misalnya, ponsel menonaktifkan kunci tombol saat menerima panggilan telepon masuk, kemudian mengaktifkan kembali kunci tombol ketika panggilan selesai."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dapatkan dan minta kompleksitas kunci layar"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Mengizinkan aplikasi mempelajari tingkat kompleksitas kunci layar (tinggi, sedang, rendah, atau tidak ada), yang menunjukkan kemungkinan rentang durasi dan jenis kunci layar. Aplikasi juga dapat menyarankan agar pengguna memperbarui kunci layar ke tingkat tertentu, tetapi pengguna dapat mengabaikan dan keluar dengan bebas. Perhatikan bahwa kunci layar tidak disimpan dalam teks biasa, sehingga aplikasi tidak mengetahui sandi yang sebenarnya."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"gunakan hardware biometrik"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Mengizinkan aplikasi menggunakan hardware biometrik untuk autentikasi"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"kelola hardware sidik jari"</string>
@@ -1088,23 +1092,23 @@
<string name="dial" msgid="1253998302767701559">"Panggil"</string>
<string name="dial_desc" msgid="6573723404985517250">"Panggil nomor telepon yang dipilih"</string>
<string name="map" msgid="5441053548030107189">"Peta"</string>
- <string name="map_desc" msgid="1836995341943772348">"Mencari alamat yang dipilih"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Cari alamat yang dipilih"</string>
<string name="browse" msgid="1245903488306147205">"Buka"</string>
<string name="browse_desc" msgid="8220976549618935044">"Buka URL yang dipilih"</string>
- <string name="sms" msgid="4560537514610063430">"Pesan"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Mengirimkan SMS ke nomor telepon yang dipilih"</string>
+ <string name="sms" msgid="4560537514610063430">"SMS"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Kirim SMS ke nomor telepon yang dipilih"</string>
<string name="add_contact" msgid="7867066569670597203">"Tambahkan"</string>
- <string name="add_contact_desc" msgid="4830217847004590345">"Menambahkan ke kontak"</string>
+ <string name="add_contact_desc" msgid="4830217847004590345">"Tambahkan ke kontak"</string>
<string name="view_calendar" msgid="979609872939597838">"Lihat"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Melihat waktu yang dipilih di kalender"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Jadwal"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Jadwalkan"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Menjadwalkan acara untuk waktu yang dipilih"</string>
- <string name="view_flight" msgid="7691640491425680214">"Lacak"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Memantau penerbangan yang dipilih"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Pantau"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Pantau penerbangan yang dipilih"</string>
<string name="translate" msgid="9218619809342576858">"Terjemahkan"</string>
<string name="translate_desc" msgid="4502367770068777202">"Terjemahkan teks yang dipilih"</string>
- <string name="define" msgid="7394820043869954211">"Tentukan"</string>
- <string name="define_desc" msgid="7910883642444919726">"Tentukan teks yang dipilih"</string>
+ <string name="define" msgid="7394820043869954211">"Definisikan"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definisikan teks yang dipilih"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Mengupdate tablet…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Mengupdate perangkat…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Menyalakan ponsel…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Memulai Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Menyalakan tablet…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Menyalakan perangkat…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimalkan penyimpanan."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap untuk melihat semua jaringan"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Hubungkan"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Semua jaringan"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Terhubung ke jaringan Wi‑Fi yang diusulkan oleh <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Perbolehkan <xliff:g id="NAME">%s</xliff:g> mengusulkan jaringan kepada Anda?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ya"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Tidak"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan aktif otomatis"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saat berada di dekat jaringan berkualitas tinggi yang tersimpan"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Jangan aktifkan kembali"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 72ce56c..f638ff8 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Leyfir forriti að fá aðgang að símaeiginleikum tækisins. Þessi heimild gerir forritinu kleift að komast að símanúmeri og auðkennum tækisins, hvort símtal er í gangi og símanúmeri viðmælanda í símtali."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"senda símtöl gegnum kerfið"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Heimilar forritinu að senda símtöl sín gegnum kerfið til að bæta gæði símtalsins."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"sjá og stjórna símtölum í gegnum kerfið."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Leyfir forritinu að sjá og stjórna hringdum símtölum í tækinu. Þar á meðal eru upplýsingar á borð við símanúmer í símtölum og stöður símtala."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"halda áfram með símtal úr öðru forriti"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Leyfir forritinu að halda áfram með símtal sem hófst í öðru forriti."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lesa símanúmer"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Leyfir forriti að eiga samskipti við NFC-merki, -spjöld og -lesara (nándarsamskipti)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"slökkva á skjálásnum"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Leyfir forriti að slökkva á símalásnum og öðrum öryggisaðgerðum tengdum aðgangsorði. Til dæmis gerir síminn lásinn óvirkan þegar símtal berst og læsist svo aftur að símtali loknu."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"fá og biðja um flókinn skjálás"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Leyfir forritinu að læra hversu flókinn skjálásinn er (erfiður, miðlungs, léttur eða enginn), sem gefur til kynna lengd og tegund skjálássins. Forritið getur einnig lagt til að notendur geri skjálásinn flóknari upp að tilteknu marki, en notendur geta valið að hunsa það og halda áfram að vafra. Hafðu í huga að skjálásinn er ekki geymdur í ódulkóðuðum texta svo forritið veit ekki nákvæmt aðgangsorð."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"nota búnað fyrir líffræðileg gögn"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Leyfir forritinu að nota búnað fyrir líffræðileg gögn til auðkenningar"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"stjórna fingrafarabúnaði"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"Eyða"</string>
<string name="inputMethod" msgid="1653630062304567879">"Innsláttaraðferð"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Textaaðgerðir"</string>
- <string name="email" msgid="4560673117055050403">"Tölvupóstur"</string>
+ <string name="email" msgid="4560673117055050403">"Senda tölvupóst"</string>
<string name="email_desc" msgid="3638665569546416795">"Senda á valið netfang"</string>
<string name="dial" msgid="1253998302767701559">"Símtal"</string>
<string name="dial_desc" msgid="6573723404985517250">"Hringja í valið símanúmer"</string>
@@ -1091,7 +1095,7 @@
<string name="map_desc" msgid="1836995341943772348">"Finna valið heimilisfang"</string>
<string name="browse" msgid="1245903488306147205">"Opna"</string>
<string name="browse_desc" msgid="8220976549618935044">"Opna valda vefslóð"</string>
- <string name="sms" msgid="4560537514610063430">"Skilaboð"</string>
+ <string name="sms" msgid="4560537514610063430">"Senda skilaboð"</string>
<string name="sms_desc" msgid="7526588350969638809">"Senda skilaboð í valið símanúmer"</string>
<string name="add_contact" msgid="7867066569670597203">"Bæta við"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Bæta við tengiliði"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Uppfærir spjaldtölvu…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Uppfærir tæki…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Síminn kveikir á sér…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android er að ræsast…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Spjaldtölvan kveikir á sér…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Tækið kveikir á sér…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Fínstillir geymslu."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Ýttu til að sjá öll netkerfi"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Tengjast"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Öll netkerfi"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Tengt við Wi-Fi net sem <xliff:g id="NAME">%s</xliff:g> stakk upp á"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Viltu leyfa <xliff:g id="NAME">%s</xliff:g> að stinga upp á neti fyrir þig?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Já"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nei"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Kveikt verður sjálfkrafa á Wi‑Fi"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Þegar þú ert nálægt vistuðu hágæðaneti"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ekki kveikja aftur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4463e11..c9c8f0c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Consente all\'applicazione di accedere alle funzioni telefoniche del dispositivo. Questa autorizzazione consente all\'applicazione di determinare il numero di telefono e gli ID dei dispositivi, se una chiamata è attiva e il numero remoto connesso da una chiamata."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"indirizzamento delle chiamate tramite il sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Consente all\'app di indirizzare le proprie chiamate tramite il sistema al fine di migliorare l\'esperienza di chiamata."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"visualizzazione e controllo delle chiamate tramite il sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Consente all\'app di visualizzare e controllare le chiamate in corso sul dispositivo. Sono incluse informazioni quali i numeri e lo stato relativi alle chiamate."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"Continuazione di una chiamata da un\'altra app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Consente all\'app di continuare una chiamata che è stata iniziata in un\'altra app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lettura dei numeri di telefono"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Consente all\'applicazione di comunicare con tag, schede e lettori NFC (Near Field Communication)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"disattivazione blocco schermo"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Consente all\'applicazione di disattivare il blocco tastiera ed eventuali protezioni tramite password associate. Ad esempio, il telefono disattiva il blocco tastiera quando riceve una telefonata in arrivo e lo riattiva al termine della chiamata."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ricezione e richiesta della complessità del blocco schermo"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Consente all\'app di conoscere il livello di complessità del blocco schermo (alto, medio, basso o nessuno), che indica l\'intervallo di caratteri possibile e il tipo di blocco schermo. L\'app può inoltre suggerire agli utenti di aggiornare il blocco schermo a un livello specifico di complessità, ma gli utenti possono ignorare liberamente il suggerimento e uscire. Tieni presente che il blocco schermo non viene memorizzato come testo non crittografato, quindi l\'app non conosce la password esatta."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Utilizzo di hardware biometrico"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Consente all\'app di utilizzare hardware biometrico per eseguire l\'autenticazione"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"gestisci hardware per il riconoscimento delle impronte digitali"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"Elimina"</string>
<string name="inputMethod" msgid="1653630062304567879">"Metodo inserimento"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Azioni testo"</string>
- <string name="email" msgid="4560673117055050403">"Invia una email"</string>
+ <string name="email" msgid="4560673117055050403">"Invia email"</string>
<string name="email_desc" msgid="3638665569546416795">"Invia un\'email all\'indirizzo selezionato"</string>
<string name="dial" msgid="1253998302767701559">"Chiama"</string>
<string name="dial_desc" msgid="6573723404985517250">"Chiama il numero di telefono selezionato"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Aggiornamento del tablet…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Aggiornamento del dispositivo…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Avvio del telefono…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Avvio di Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Avvio del tablet…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Avvio del dispositivo…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ottimizzazione archiviazione."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tocca per vedere tutte le reti"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connetti"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tutte le reti"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"È stata stabilita la connessione alla rete Wi‑Fi proposta da <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vuoi consentire all\'app <xliff:g id="NAME">%s</xliff:g> di proporti reti?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sì"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Il Wi‑Fi verrà attivato automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando ti trovi nell\'area di una rete salvata di alta qualità"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Non riattivare"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 3f0c0b8..26f6988f 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"מאפשר לאפליקציה לגשת לתכונות הטלפון של המכשיר. אישור זה מתיר לאפליקציה לגלות את מספר הטלפון ואת זיהויי המכשיר, האם שיחה פעילה ואת המספר המרוחק המחובר באמצעות שיחה."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ניתוב שיחות דרך המערכת"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"מאפשרת לאפליקציה לנתב את השיחות דרך המערכת כדי לשפר את חוויית השיחה."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ניתן להציג שיחות ולשלוט בהן באמצעות המערכת."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"מאפשר לאפליקציה להציג שיחות נוכחיות ולשלוט בהן במכשיר. זה כולל פרטים כמו מספרי שיחה של שיחות ומצב השיחה."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"המשך שיחה מאפליקציה אחרת"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"הרשאה זו מתירה לאפליקציה להמשיך שיחה שהחלה באפליקציה אחרת."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"גישה למספרי הטלפון"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"מאפשר לאפליקציה נהל תקשורת עם תגים, כרטיסים וקוראים מסוג \'תקשורת מטווח קצר\'."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ביטול נעילת המסך שלך"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"מאפשר לאפליקציה להשבית את נעילת המקשים וכל אמצעי אבטחה משויך המבוסס על סיסמה. לדוגמה, הטלפון משבית את נעילת המקשים בעת קבלה של שיחת טלפון נכנסת, ולאחר מכן מפעיל מחדש את נעילת המקשים עם סיום השיחה."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"קבלה ובקשה של מורכבות לנעילת מסך"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"מאפשרת לאפליקציה ללמוד את רמת המורכבות של נעילת המסך (גבוהה, בינונית, נמוכה או ללא). רמה זו מציינת את הטווח האפשרי של אורך וסוג של נעילת המסך. האפליקציה יכולה גם להציע למשתמשים שיעדכנו את נעילת המסך ברמה מסוימת, אבל המשתמשים יכולים להתעלם מההצעה ולנווט לפריט אחר כרצונם. יש לשים לב שנעילת המסך לא מאוחסנת ב-plaintext, ולכן האפליקציה לא יודעת מה הסיסמה המדויקת."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"שימוש בחומרה ביומטרית"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"מאפשרת לאפליקציה להשתמש בחומרה ביומטרית לצורך אימות"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ניהול חומרה של טביעות אצבעות"</string>
@@ -1123,28 +1127,28 @@
<string name="deleteText" msgid="6979668428458199034">"מחיקה"</string>
<string name="inputMethod" msgid="1653630062304567879">"שיטת קלט"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"פעולות טקסט"</string>
- <string name="email" msgid="4560673117055050403">"אימייל"</string>
+ <string name="email" msgid="4560673117055050403">"התכתבות באימייל"</string>
<string name="email_desc" msgid="3638665569546416795">"שליחת אימייל לכתובת שנבחרה"</string>
- <string name="dial" msgid="1253998302767701559">"שיחה"</string>
+ <string name="dial" msgid="1253998302767701559">"ביצוע שיחה"</string>
<string name="dial_desc" msgid="6573723404985517250">"התקשרות למספר הטלפון שנבחר"</string>
- <string name="map" msgid="5441053548030107189">"מפה"</string>
- <string name="map_desc" msgid="1836995341943772348">"מיקום הכתובת שנבחרה"</string>
+ <string name="map" msgid="5441053548030107189">"צפייה במפה"</string>
+ <string name="map_desc" msgid="1836995341943772348">"איתור הכתובת שנבחרה"</string>
<string name="browse" msgid="1245903488306147205">"פתיחה"</string>
<string name="browse_desc" msgid="8220976549618935044">"פתיחה של כתובת האתר שנבחרה"</string>
- <string name="sms" msgid="4560537514610063430">"הודעה"</string>
+ <string name="sms" msgid="4560537514610063430">"התכתבות בהודעות"</string>
<string name="sms_desc" msgid="7526588350969638809">"שליחת הודעה למספר הטלפון שנבחר"</string>
<string name="add_contact" msgid="7867066569670597203">"הוספה"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"הוספה לאנשי הקשר"</string>
- <string name="view_calendar" msgid="979609872939597838">"תצוגה"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"הצגת הזמן שנבחר ביומן"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"לוח זמנים"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"תזמון אירוע לזמן שנבחר"</string>
+ <string name="view_calendar" msgid="979609872939597838">"צפייה"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"הצגת התאריך שנבחר ביומן"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"קביעת מועד"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"קביעת מועד לאירוע בתאריך שנבחר"</string>
<string name="view_flight" msgid="7691640491425680214">"מעקב"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"ניהול מעקב אחר הטיסה שנבחרה"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"מעקב אחרי הטיסה שנבחרה"</string>
<string name="translate" msgid="9218619809342576858">"תרגום"</string>
<string name="translate_desc" msgid="4502367770068777202">"תרגום הטקסט שנבחר"</string>
- <string name="define" msgid="7394820043869954211">"הגדרה"</string>
- <string name="define_desc" msgid="7910883642444919726">"הגדרת הטקסט שנבחר"</string>
+ <string name="define" msgid="7394820043869954211">"מציאת הפירוש"</string>
+ <string name="define_desc" msgid="7910883642444919726">"פירוש הטקסט שנבחר"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"אין מספיק שטח אחסון עבור המערכת. ודא שיש לך שטח פנוי בגודל 250MB התחל שוב."</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"הטאבלט מתעדכן…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"הפעלת המכשיר מתחילה…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"הפעלת הטלפון מתחילה…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"הפעלת Android מתחילה…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"הפעלת הטאבלט מתחילה…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"הפעלת המכשיר מתחילה…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"מתבצעת אופטימיזציה של האחסון."</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"יש להקיש כדי לראות את כל הרשתות"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"התחבר"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"כל הרשתות"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"יש חיבור לרשת Wi‑Fi שהוצעה על ידי <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"האם ברצונך לקבל הצעות לרשתות מ-<xliff:g id="NAME">%s</xliff:g>?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"כן"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"לא"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ה-Wi-Fi יופעל אוטומטית"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"כשתימצאו בקרבת רשת באיכות גבוהה ששמרתם"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"אל תפעיל שוב"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e069e33..45587cb 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"端末の電話機能へのアクセスをアプリに許可します。これにより、電話番号、端末ID、通話中かどうか、通話相手の電話番号をアプリから特定できるようになります。"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"システム経由での通話転送"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"通話環境の改善のために、システム経由での通話転送をアプリに許可します。"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"システム経由の通話の表示と操作。"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"デバイスで進行中の通話の表示と操作をアプリに許可します。通話の電話番号や状態などの情報も含まれます。"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"別のアプリでの通話の続行"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"別のアプリで通話を続行することをこのアプリに許可します。"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"電話番号の読み取り"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"NFCタグ、カード、リーダーとの通信をアプリに許可します。"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"画面ロックの無効化"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"キーロックとキーロックに関連付けられたパスワードのセキュリティを無効にすることをアプリに許可します。たとえば、かかってきた電話を受ける際にキーロックを無効にし、通話が終了したらキーロックを再度有効にする場合などに使用します。"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"画面ロックの複雑さの入手とリクエスト"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"このアプリに画面ロックの複雑さレベル(高、中、低、なし)を認識することを許可します。複雑さレベルは、画面ロックの文字数の範囲やタイプを示すものです。アプリはユーザーに一定レベルまで画面ロックを更新するようすすめることもできますが、ユーザーは無視したり別の操作を行ったりできます。画面ロックは平文で保存されないため、アプリが正確なパスワードを知ることはありません。"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"生体認証ハードウェアの使用"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"生体認証ハードウェアを認証に使用することをアプリに許可します"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"指紋ハードウェアの管理"</string>
@@ -1097,9 +1101,9 @@
<string name="add_contact_desc" msgid="4830217847004590345">"連絡先に追加します"</string>
<string name="view_calendar" msgid="979609872939597838">"表示"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"カレンダーで選択した日時を表示します"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"スケジュール"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"予定を作成"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"選択した日時に予定を設定します"</string>
- <string name="view_flight" msgid="7691640491425680214">"トラック"</string>
+ <string name="view_flight" msgid="7691640491425680214">"フライト"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"選択したフライトをチェックします"</string>
<string name="translate" msgid="9218619809342576858">"翻訳"</string>
<string name="translate_desc" msgid="4502367770068777202">"選択したテキストを翻訳します"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"タブレットの更新中…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"端末の更新中…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"スマートフォンの起動中…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Androidの起動中…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"タブレットの起動中…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"端末の起動中…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ストレージを最適化しています。"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"すべてのネットワークを表示するにはタップします"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"接続"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"すべてのネットワーク"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> で推奨される Wi-Fi ネットワークに接続"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"ネットワークを推奨することを <xliff:g id="NAME">%s</xliff:g> に許可しますか?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"はい"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"いいえ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi は自動的にオンになります"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"高品質の保存済みネットワークの検出時"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"再度オンにしない"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 3be3093..07df9fc 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"აპს შეეძლება ჰქონდეს წვდომა მოწყობილობის სატელეფონო ფუნქციებზე. აპმა მსგავსი უფლებით შეძლებს დაადგინოს ტელეფონის ნომერი, მისი სერიული გამოცემა, აქტიური ზარი, დაკავშირებული ნომერი და მსგავსი."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ზარების სისტემის მეშვეობით მარშრუტიზაცია"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"დარეკვის ხარისხის გაუმჯობესების მიზნით, აპს ზარების სისტემის მეშვეობით მარშრუტიზაციის საშუალებას აძლევს."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ზარების ნახვა და გაკონტროლება სისტემის მეშვეობით."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"საშუალებას აძლევს აპს, იხილოს და გააკონტროლოს მიმდინარე ზარები მოწყობილობაზე. აღნიშნული მოიცავს ისეთ ინფორმაციას, როგორიცაა ზარებთან დაკავშირებული აბონენტების ნომრები და ზარების მდგომარეობა."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ზარის სხვა აპიდან გაგრძელება"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ნებას რთავს აპს, გააგრძელოს ზარი, რომელიც სხვა აპშია წამოწყებული."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ტელეფონის ნომრების წაკითხვა"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"აპს შეეძლება ახლო მოქმედების რადიოკავშირის (NFC) მეშვეობით ტეგების, ბარათებისა და წამკითხველების შემცველი მონაცემების მიმოცვლა."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"თქვენი ეკრანის ბლოკის გათიშვა"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"შეეძლება კლავიატურის დაბლოკვისა და პაროლით უზრუნველყოფილი ნებისმიერი უსაფრთხოების ფუნქციის დეაქტივაცია. მაგალითად, ტელეფონი შემომავალი ზარის დროს აუქმებს კლავიატურის დაბლოკვას და კვლავ ააქტიურებს მას, როგორც კი ზარი დასრულდება."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ეკრანის დაბლოკვის მეთოდის სირთულის მიღება და მოთხოვნა"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"საშუალებას აძლევს აპს, შეიტყოს ეკრანის დაბლოკვის მეთოდის სირთულე (მაღალი, საშუალო, დაბალი ან არანაირი), რისი მეშვეობითაც შესაძლებელია ეკრანის დაბლოკვის მეთოდის სიგრძის შესაძლო დიაპაზონისა და ტიპის განსაზღვრა. გარდა ამისა, აპს შეუძლია მომხმარებლებისთვის ეკრანის დაბლოკვის მეთოდის გარკვეულ დონემდე გაძლიერების შეთავაზება, თუმცა მომხმარებლებს შეეძლებათ აღნიშნული შეტყობინების უგულებელყოფა და სხვა ეკრანზე გადასვლა. გაითვალისწინეთ, რომ ეკრანის დაბლოკვის მეთოდი არ ინახება ჩვეულებრივი ტექსტის სახით, ამიტომ აპს არ ეცოდინება ზუსტი პაროლი."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ბიომეტრიული აპარატის გამოყენება"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს ბიომეტრიული აპარატი"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"თითის ანაბეჭდის აპარატის მართვა"</string>
@@ -1083,11 +1087,11 @@
<string name="deleteText" msgid="6979668428458199034">"წაშლა"</string>
<string name="inputMethod" msgid="1653630062304567879">"შეყვანის მეთოდი"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"ქმედებები ტექსტზე"</string>
- <string name="email" msgid="4560673117055050403">"ელფოსტა"</string>
+ <string name="email" msgid="4560673117055050403">"ელფოსტის გაგზავნა"</string>
<string name="email_desc" msgid="3638665569546416795">"არჩეულ მისამართზე ელფოსტის გაგზავნა"</string>
<string name="dial" msgid="1253998302767701559">"ზარი"</string>
<string name="dial_desc" msgid="6573723404985517250">"არჩეულ ტელეფონის ნომერზე დარეკვა"</string>
- <string name="map" msgid="5441053548030107189">"რუკა"</string>
+ <string name="map" msgid="5441053548030107189">"რუკის გახსნა"</string>
<string name="map_desc" msgid="1836995341943772348">"არჩეული მისამართის მდებარეობის დადგენა"</string>
<string name="browse" msgid="1245903488306147205">"გახსნა"</string>
<string name="browse_desc" msgid="8220976549618935044">"არჩეული URL-ის გახსნა"</string>
@@ -1097,9 +1101,9 @@
<string name="add_contact_desc" msgid="4830217847004590345">"დამატება"</string>
<string name="view_calendar" msgid="979609872939597838">"ნახვა"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"არჩეული დროის კალენდარში ნახვა"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"განრიგი"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"დაგეგმვა"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"მოვლენის არჩეული დროისთვის დაგეგმვა"</string>
- <string name="view_flight" msgid="7691640491425680214">"ჩანაწერი"</string>
+ <string name="view_flight" msgid="7691640491425680214">"მიდევნება"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"არჩეული ფრენისთვის თვალის მიდევნება"</string>
<string name="translate" msgid="9218619809342576858">"თარგმნა"</string>
<string name="translate_desc" msgid="4502367770068777202">"არჩეული ტექსტის თარგმნა"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"მიმდინარეობს ტაბლეტის განახლება…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"მიმდინარეობს მოწყობილობის განახლება…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"მიმდინარეობს ტელეფონის ჩართვა…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android იწყება…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"მიმდინარეობს ტაბლეტის ჩართვა…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"მიმდინარეობს მოწყობილობის ჩართვა…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"მეხსიერების ოპტიმიზირება."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"შეეხეთ ყველა ქსელის სანახავად"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"დაკავშირება"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ყველა ქსელი"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"დაკავშირებულია <xliff:g id="NAME">%s</xliff:g>-ის მიერ შემოთავაზებულ Wi‑Fi ქსელთან"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"გსურთ, საშუალება მისცეთ <xliff:g id="NAME">%s</xliff:g>-ს, შემოგთავაზოთ ქსელები?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"დიახ"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"არა"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi ავტომატურად ჩაირთვება"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"როცა შენახულ მაღალხარისხიან ქსელებთან ახლოს იმყოფებით"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ხელახლა ნუ ჩართავ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 0b4ff1c..c622094 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Қолданбаға құрылғыдағы телефон функцияларына кіру мүмкіндігін береді. Бұл рұқсат қолданбаға телефон нөмірі, құрылғы жеке анықтағышы, қоңырау белсенділігі және сол қоңырауға байланысты қашықтағы нөмірді анықтау мүмкіндігін береді."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"қоңырауларды жүйе арқылы бағыттау"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Қоңырау шалу тәжірибесін жақсарту үшін қолданба қоңырауларды жүйе арқылы бағыттай алады."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"жүйе арқылы қоңырауларды көру және басқару."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Қолданбаға құрылғыдағы қазіргі қоңырауларды көруге және басқаруға мүмкіндік береді. Бұл – қоңырау шалу нөмірлері және қоңыраулардың күйі сияқты ақпаратқа қатысты."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"басқа қолданбадағы қоңырауды жалғастыру"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Қолданбаға басқа қолданбадағы қоңырауды жалғастыруға рұқсат береді."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"телефон нөмірлерін оқу"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Қолданбаға NFC белгілерімен, карталармен және оқу құралдарымен байланысуға рұқсат береді."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"экран бекітпесін істен шығару"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Қолданбаларға кілтперне және басқа кілтсөзге қатысты қауіпсіздік шараларын өшіру мүмкіндігін береді. Мысалы, телефон кіріс қоңырауларын алғанда кілтпернені өшіреді және қоңырау аяқталғанда қайта қосады."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"экранды құлыптау күрделілігі туралы дерек алу және оны сұрау"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Қолданбаға экранды құлыптау күрделілігінің деңгейін (жоғары, орташа, төмен немесе жоқ), соның ішінде ұзақтығы мен түрін көрсетеді. Сонымен қатар қолданба пайдаланушыларға экранды құлыптауды белгілі бір деңгейге жаңартуды ұсынады. Бірақ бұл ұсыныстарды елемеуге болады. Экранды құлпы қарапайым мәтін түрінде сақталмайтынын және қолданбаға белгісіз болатынын ескеріңіз."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"биометрикалық жабдықты пайдалану"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Аутентификациялау үшін қолданбаға биометрикалық жабдықты пайдалануға рұқсат береді"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"саусақ ізі жабдығын басқару"</string>
@@ -1083,23 +1087,23 @@
<string name="deleteText" msgid="6979668428458199034">"Жою"</string>
<string name="inputMethod" msgid="1653630062304567879">"Енгізу әдісі"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Мәтін әрекеттері"</string>
- <string name="email" msgid="4560673117055050403">"Электрондық пошта"</string>
- <string name="email_desc" msgid="3638665569546416795">"Таңдалған электрондық пошта мекенжайына хабар жіберу"</string>
+ <string name="email" msgid="4560673117055050403">"Эл. поштаны ашу"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Таңдалған мекенжайға хабар жіберу"</string>
<string name="dial" msgid="1253998302767701559">"Қоңырау шалу"</string>
<string name="dial_desc" msgid="6573723404985517250">"Таңдалған телефон нөміріне қоңырау шалу"</string>
- <string name="map" msgid="5441053548030107189">"Map"</string>
- <string name="map_desc" msgid="1836995341943772348">"Таңдалған мекенжайдың орналасқан жерін анықтау"</string>
+ <string name="map" msgid="5441053548030107189">"Картаны ашу"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Таңдалған мекенжайды табу"</string>
<string name="browse" msgid="1245903488306147205">"Ашу"</string>
<string name="browse_desc" msgid="8220976549618935044">"Таңдалған URL мекенжайын ашу"</string>
- <string name="sms" msgid="4560537514610063430">"Хабар"</string>
+ <string name="sms" msgid="4560537514610063430">"Хабар жазу"</string>
<string name="sms_desc" msgid="7526588350969638809">"Таңдалған телефон нөміріне хабар жіберу"</string>
<string name="add_contact" msgid="7867066569670597203">"Енгізу"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Контактілер тізіміне енгізу"</string>
<string name="view_calendar" msgid="979609872939597838">"Көру"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Күнтізбеде таңдалған уақытты көру"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Кесте"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Таңдалған уақытқа іс-шараны жоспарлау"</string>
- <string name="view_flight" msgid="7691640491425680214">"Бақылау"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Таңдалған уақытты күнтізбеден көру"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Жоспарлау"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Таңдалған уақытқа іс-шара жоспарлау"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Қадағалау"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Таңдалған ұшу рейсін қадағалау"</string>
<string name="translate" msgid="9218619809342576858">"Аудару"</string>
<string name="translate_desc" msgid="4502367770068777202">"Таңдалған мәтінді аудару"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Планшет жаңартылуда…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Құрылғы жаңартылуда…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон іске қосылуда…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android іске қосылуда…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Планшет іске қосылуда…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Құрылғы іске қосылуда…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Қойманы оңтайландыру."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Барлық желілерді көру үшін түртіңіз"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Қосылу"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Барлық желілер"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ұсынған Wi‑Fi желісіне жалғанды"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> сізге желі ұсынғанын қалайсыз ба?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Иә"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Жоқ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi автоматты түрде қосылады"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Сақталған жоғары сапалы желіге жақын болғанда"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Қайта қоспау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index cd2af7b..ccc243d 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ឲ្យកម្មវិធីចូលដំណើរការលក្ខណៈទូរស័ព្ទនៃឧបករណ៍។ សិទ្ធិនេះឲ្យកម្មវិធីកំណត់លេខទូរស័ព្ទ និងលេខសម្គាល់ឧបករណ៍ ថាតើការហៅសកម្ម និងលេខពីចម្ងាយបានភ្ជាប់ដោយការហៅ។"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"បញ្ជូនការហៅទូរសព្ទតាមរយៈប្រព័ន្ធ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"អនុញ្ញាតឲ្យកម្មវិធីនេះបញ្ជូនការហៅទូរសព្ទរបស់វាតាមរយៈប្រព័ន្ធ ដើម្បីធ្វើឲ្យការហៅទូរសព្ទប្រសើរជាងមុន។"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"មើល និងគ្រប់គ្រងការហៅទូរសព្ទតាមរយៈប្រព័ន្ធ។"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"អនុញ្ញាតឱ្យកម្មវិធីមើល និងគ្រប់គ្រងការហៅទូរសព្ទដែលកំពុងដំណើរការនៅលើឧបករណ៍។ សកម្មភាពនេះរួមមានព័ត៌មានដូចជា លេខទូរសព្ទសម្រាប់ការហៅទូរសព្ទ និងស្ថានភាពនៃការហៅទូរសព្ទជាដើម។"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"បន្តការហៅទូរសព្ទពីកម្មវិធីផ្សេង"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"អនុញ្ញាតឱ្យកម្មវិធីបន្តការហៅទូរសព្ទ ដែលបានចាប់ផ្តើមនៅក្នុងកម្មវិធីផ្សេង។"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"អានលេខទូរសព្ទ"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ឲ្យកម្មវិធីទាក់ទងជាមួយស្លាក (NFC) កាត និងកម្មវិធីអាន។"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"បិទការចាក់សោអេក្រង់របស់អ្នក"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ឲ្យកម្មវិធីបិទការចាក់សោសុវត្ថិភាពពាក្យសម្ងាត់ដែលបានភ្ជាប់ណាមួយ។ ឧទាហរណ៍ត្រឹមត្រូវនៃការបិទទូរស័ព្ទពេលទទួលការហៅចូល បន្ទាប់មបើកសោពេលការហៅបានបញ្ចប់។"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ទទួល និងស្នើសុំភាពស្មុគស្មាញនៃការចាក់សោអេក្រង់"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"អនុញ្ញាតឱ្យកម្មវិធីរៀនអំពីកម្រិតស្មុគស្មាញការនៃការចាក់សោអេក្រង់ (ខ្ពស់ មធ្យម ទាប ឬគ្មាន) ដែលបញ្ជាក់អំពីប្រវែង និងប្រភេទនៃការចាក់សោអេក្រង់។ កម្មវិធីនេះក៏អាចណែនាំឱ្យអ្នកប្រើប្រាស់ធ្វើបច្ចុប្បន្នភាពការចាក់សោអេក្រង់ទៅកម្រិតជាក់លាក់ផងដែរ ប៉ុន្តែអ្នកប្រើប្រាស់អាចមិនអើពើនឹងការណែនាំនេះដោយសេរី។ សូមចំណាំថា ការចាក់សោអេក្រង់មិនត្រូវបានរក្សាទុកជាអត្ថបទធម្មតាទេ ដូច្នេះកម្មវិធីនេះមិនស្គាល់ពាក្យសម្ងាត់ពិតប្រាកដឡើយ។"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ប្រើឧបករណ៍ស្កេនស្នាមម្រាមដៃ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"អនុញ្ញាតឱ្យកម្មវិធីប្រើឧបករណ៍ស្កេនស្នាមម្រាមដៃសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"គ្រប់គ្រងផ្នែករឹងស្នាមម្រាមដៃ"</string>
@@ -1183,6 +1187,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ថេប្លេតកំពុងដំឡើងជំនាន់…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ឧបករណ៍កំពុងដំឡើងជំនាន់…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ទូរសព្ទកំពុងចាប់ផ្ដើម…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android កំពុងចាប់ផ្ដើម…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ថេប្លេតកំពុងចាប់ផ្ដើម…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ឧបករណ៍កំពុងចាប់ផ្ដើម…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"កំពុងធ្វើឲ្យឧបករណ៍ផ្ទុកមានប្រសិទ្ធភាព។"</string>
@@ -1241,6 +1246,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ចុចដើម្បីមើលបណ្តាញទាំងអស់"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"ភ្ជាប់"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"បណ្ដាញទាំងអស់"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"បានភ្ជាប់បណ្ដាញ Wi‑Fi ដែលបានណែនាំដោយ <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"តើអ្នកចង់អនុញ្ញាតឱ្យ <xliff:g id="NAME">%s</xliff:g> ណែនាំបណ្ដាញសម្រាប់អ្នកដែរទេ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"បាទ/ចាស"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ទេ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi នឹងបើកដោយស្វ័យប្រវត្តិ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"នៅពេលដែលអ្នកនៅជិតបណ្តាញគុណភាពខ្ពស់ដែលបានរក្សាទុក"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"កុំបើកឡើងវិញ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 44010d2..835070a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ಸಾಧನದ ಫೋನ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ಫೋನ್ ಸಂಖ್ಯೆ ಮತ್ತು ಸಾಧನದ ID ಗಳನ್ನು ನಿರ್ಧರಿಸಲು, ಕರೆಯು ಸಕ್ರಿಯವಾಗಿದೆಯೇ ಮತ್ತು ಕರೆಯ ಮೂಲಕ ರಿಮೋಟ್ ಸಂಖ್ಯೆಯು ಸಂಪರ್ಕಗೊಂಡಿವೆಯೇ ಎಂಬುದನ್ನೂ ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸುತ್ತದೆ."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಿ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ಕರೆಯ ಅನುಭವವನ್ನು ಸುಧಾರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ನ ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಲು ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ಸಿಸ್ಟಂ ಮೂಲಕ ಕರೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ಸಾಧನದಲ್ಲಿನ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಮತ್ತು ನಿಯಂತ್ರಿಸಲು ಆ್ಯಪ್ ಅನುಮತಿಸುತ್ತದೆ. ಕರೆಗಳಿಗೆ ಸಂಬಂಧಿಸಿದ ಕರೆ ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಕರೆ ಮಾಡಿದ ರಾಜ್ಯದಂತಹ ಮಾಹಿತಿಯನ್ನು ಇದು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ಮತ್ತೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಮೂಲಕ ಕರೆಯನ್ನು ಮುಂದುವರಿಸಿ"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ಮತ್ತೊಂದು ಅಪ್ಲಿಕೇಶನ್ನಲ್ಲಿ ಪ್ರಾರಂಭವಾದ ಕರೆಯನ್ನು ಮುಂದುವರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡಿ."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ಫೋನ್ ಸಂಖ್ಯೆಗಳನ್ನು ಓದಿ"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ಸಮೀಪದ ಕ್ಷೇತ್ರ ಸಂವಹನ (NFC) ಟ್ಯಾಗ್ಗಳು, ಕಾರ್ಡ್ಗಳು, ಮತ್ತು ಓದುಗರನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ಕೀಲಾಕ್ ಮತ್ತು ಯಾವುದೇ ಸಂಬಂಧಿತ ಭದ್ರತಾ ಪಾಸ್ವರ್ಡ್ ಭದ್ರತೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಒಳಬರುವ ಕರೆಯನ್ನು ಸ್ವೀಕರಿಸುವಾಗ ಕೀಲಾಕ್ ಅನ್ನು ಫೋನ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ, ನಂತರ ಕರೆಯು ಅಂತ್ಯಗೊಂಡಾಗ ಕೀಲಾಕ್ ಅನ್ನು ಮರು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಂಕೀರ್ಣತೆಯನ್ನು ಪಡೆದುಕೊಳ್ಳಿ ಮತ್ತು ವಿನಂತಿಸಿ"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಂಕೀರ್ಣತೆ ಮಟ್ಟವನ್ನು ತಿಳಿದುಕೊಳ್ಳಲು ಅನುಮತಿಸುತ್ತದೆ (ಹೆಚ್ಚು, ಮಧ್ಯಮ, ಅಥವಾ ಕಡಿಮೆ ಯಾವುದೂ ಅಲ್ಲ), ಇದು ಉದ್ದದ ಸಂಭವನೀಯ ಶ್ರೇಣಿ ಮತ್ತು ಸ್ಕ್ರೀನ್ ಲಾಕ್ನ ವಿಧವನ್ನು ಸೂಚಿಸುತ್ತದೆ. ಬಳಕೆದಾರರು ನಿರ್ದಿಷ್ಟ ಮಟ್ಟದವರೆಗೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು ಎಂಬುದಾಗಿ ಕೂಡ ಆ್ಯಪ್ ಬಳಕೆದಾರರಿಗೆ ಸಲಹೆ ಮಾಡುತ್ತದೆ ಆದರೆ ಬಳಕೆದಾರರು ಮುಕ್ತವಾಗಿ ತಿರಸ್ಕರಿಸಬಹುದು ಮತ್ತು ನ್ಯಾವಿಗೇಟ್ ಮಾಡಬಹುದು. ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಖಾಲಿಪಠ್ಯದಲ್ಲಿ ಸಂಗ್ರಹಿಸಿಲ್ಲ ಎಂಬುದನ್ನು ಗಮನಿಸಿ ಇದರಿಂದ ಆ್ಯಪ್ಗೆ ಸರಿಯಾದ ಪಾಸ್ವರ್ಡ್ ಗೊತ್ತಿರುವುದಿಲ್ಲ."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಬಳಸಿ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ಬೆರಳಚ್ಚು ಹಾರ್ಡ್ವೇರ್ ನಿರ್ವಹಿಸಿ"</string>
@@ -1083,25 +1087,25 @@
<string name="deleteText" msgid="6979668428458199034">"ಅಳಿಸಿ"</string>
<string name="inputMethod" msgid="1653630062304567879">"ಇನ್ಪುಟ್ ವಿಧಾನ"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"ಪಠ್ಯದ ಕ್ರಮಗಳು"</string>
- <string name="email" msgid="4560673117055050403">"ಇಮೇಲ್"</string>
+ <string name="email" msgid="4560673117055050403">"ಇಮೇಲ್ ಮಾಡಿ"</string>
<string name="email_desc" msgid="3638665569546416795">"ಆಯ್ಕೆಮಾಡಿದ ವಿಳಾಸಕ್ಕೆ ಇಮೇಲ್ ಮಾಡಿ"</string>
- <string name="dial" msgid="1253998302767701559">"ಕರೆ"</string>
+ <string name="dial" msgid="1253998302767701559">"ಕರೆ ಮಾಡಿ"</string>
<string name="dial_desc" msgid="6573723404985517250">"ಆಯ್ಕೆಮಾಡಿದ ಫೋನ್ ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿ"</string>
<string name="map" msgid="5441053548030107189">"ನಕ್ಷೆ"</string>
<string name="map_desc" msgid="1836995341943772348">"ಆಯ್ಕೆ ಮಾಡಿದ ವಿಳಾಸವನ್ನು ಗುರುತಿಸಿ"</string>
- <string name="browse" msgid="1245903488306147205">"ತೆರೆ"</string>
+ <string name="browse" msgid="1245903488306147205">"ತೆರೆಯಿರಿ"</string>
<string name="browse_desc" msgid="8220976549618935044">"ಆಯ್ಕೆ ಮಾಡಿದ URL ತೆರೆಯಿರಿ"</string>
- <string name="sms" msgid="4560537514610063430">"ಸಂದೇಶ"</string>
+ <string name="sms" msgid="4560537514610063430">"ಸಂದೇಶ ಕಳುಹಿಸಿ"</string>
<string name="sms_desc" msgid="7526588350969638809">"ಆಯ್ಕೆಮಾಡಿದ ಫೋನ್ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಕಳುಹಿಸಿ"</string>
<string name="add_contact" msgid="7867066569670597203">"ಸೇರಿಸಿ"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"ಸಂಪರ್ಕಗಳಿಗೆ ಸೇರಿಸಿ"</string>
<string name="view_calendar" msgid="979609872939597838">"ವೀಕ್ಷಿಸಿ"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ಕ್ಯಾಲೆಂಡರ್ನಲ್ಲಿ ಆಯ್ಕೆಮಾಡಿದ ಸಮಯವನ್ನು ವೀಕ್ಷಿಸಿ"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"ಅವಧಿ"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"ಆಯ್ಕೆ ಮಾಡಿದ ಸಮಯಕ್ಕೆ ಈವೆಂಟ್ ನಿಗದಿಪಡಿಸಿ"</string>
- <string name="view_flight" msgid="7691640491425680214">"ಟ್ರ್ಯಾಕ್"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ಸಮಯ ನಿಗದಿಗೊಳಿಸಿ"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"ಆಯ್ಕೆ ಮಾಡಿದ ಸಮಯಕ್ಕೆ ಈವೆಂಟ್ ನಿಗದಿಗೊಳಿಸಿ"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ಟ್ರ್ಯಾಕ್ ಮಾಡಿ"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ಆಯ್ಕೆಮಾಡಿದ ವಿಮಾನವನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಿ"</string>
- <string name="translate" msgid="9218619809342576858">"ಅನುವಾದ"</string>
+ <string name="translate" msgid="9218619809342576858">"ಅನುವಾದ ಮಾಡಿ"</string>
<string name="translate_desc" msgid="4502367770068777202">"ಆಯ್ಕೆಮಾಡಿದ ಪಠ್ಯವನ್ನು ಅನುವಾದಿಸಿ"</string>
<string name="define" msgid="7394820043869954211">"ವಿವರಿಸಿ"</string>
<string name="define_desc" msgid="7910883642444919726">"ಆಯ್ಕೆಮಾಡಿದ ಪಠ್ಯವನ್ನು ವಿವರಿಸಿ"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ಸಾಧನವನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ಫೋನ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ಟ್ಯಾಬ್ಲೆಟ್ ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ಸಾಧನವನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ಸಂಗ್ರಹಣೆಯನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ಎಲ್ಲಾ ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"ಸಂಪರ್ಕಿಸಿ"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ಎಲ್ಲಾ ನೆಟ್ವರ್ಕ್ಗಳು"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ಅವರಿಂದ ಸೂಚಿತ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> ಅವರು ನಿಮಗಾಗಿ ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಪ್ರಸ್ತಾಪಿಸುವುದೇ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ಹೌದು"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ಇಲ್ಲ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ವೈ‑ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಆಗುತ್ತದೆ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ನೀವು ಉಳಿಸಿದ ಅಧಿಕ ಗುಣಮಟ್ಟದ ನೆಟ್ವರ್ಕ್ ಸಮೀಪದಲ್ಲಿದ್ದಾಗ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ಮತ್ತೆ ಆನ್ ಮಾಡಲು ಹಿಂತಿರುಗಬೇಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index bdc50bf..8886815 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"앱이 기기의 휴대전화 기능에 접근할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 전화번호 및 기기의 ID, 활성 통화인지 여부, 통화가 연결된 원격 번호 등을 확인할 수 있습니다."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"시스템을 통해 통화 연결"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"통화 환경을 개선하기 위해 앱이 시스템을 통해 통화를 연결하도록 허용합니다."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"시스템을 통해 통화 확인 및 제어"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"앱이 기기에서 진행 중인 통화를 확인 및 제어하도록 허용합니다. 여기에는 통화에 사용된 전화번호 및 통화 상태 등의 정보가 포함됩니다."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"다른 앱에서 전화 받기"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"다른 앱에서 수신한 전화를 계속하려면 앱을 허용합니다."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"전화번호 읽기"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"앱이 NFC(근거리 무선 통신) 태그, 카드 및 리더와 통신할 수 있도록 허용합니다."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"화면 잠금 사용 중지"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"앱이 키 잠금 및 관련 비밀번호 보안을 사용중지할 수 있도록 허용합니다. 예를 들어, 휴대전화가 수신전화를 받을 때 키 잠금을 사용중지했다가 통화가 끝나면 키 잠금을 다시 사용할 수 있습니다."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"화면 잠금 복잡도 확인 및 요청"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"앱이 화면 잠금 길이와 유형의 가능한 범위를 나타내는 잠금 화면 복잡도 수준(높음, 보통, 낮음 또는 없음)을 파악하도록 허용합니다. 앱이 사용자에게 화면 잠금을 특정 수준으로 업데이트할 것을 제안할 수도 있지만, 사용자는 자유롭게 이를 무시하고 다른 곳으로 이동할 수 있습니다. 화면 잠금은 일반 텍스트로 저장되지 않으므로 앱에서 정확한 비밀번호를 알 수 없습니다."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"생체 인식 하드웨어 사용"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"앱에서 생체 인식 하드웨어를 인증에 사용하도록 허용합니다."</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"지문 하드웨어 관리"</string>
@@ -1096,9 +1100,9 @@
<string name="add_contact" msgid="7867066569670597203">"추가"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"연락처에 추가"</string>
<string name="view_calendar" msgid="979609872939597838">"보기"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"캘린더에서 선택한 시간 보기"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"일정"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"선택한 시간의 일정 예약"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"선택한 시간 캘린더에서 보기"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"일정 만들기"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"선택한 시간으로 일정 만들기"</string>
<string name="view_flight" msgid="7691640491425680214">"추적"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"선택한 항공편 추적"</string>
<string name="translate" msgid="9218619809342576858">"번역"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"태블릿을 업데이트하는 중…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"기기를 업데이트하는 중…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"휴대전화를 시작하는 중…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android가 시작되는 중…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"태블릿을 시작하는 중…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"기기를 시작하는 중…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"저장소 최적화 중"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"모든 네트워크를 보려면 탭하세요."</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"연결"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"모든 네트워크"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>에서 제안한 Wi‑Fi 네트워크에 연결됨"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g>에서 네트워크를 제안하도록 허용하시겠습니까?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"예"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"아니요"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi가 자동으로 사용 설정됨"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"저장된 고품질 네트워크가 가까이 있는 경우"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"다시 사용 설정하지 않음"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index f6e9b72..de49741 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Колдонмого түзмөктүн чалуу мүмкүнчүлүктөрүнө жетки алуу уруксатын берет. Бул уруксат колдонмого, телефондун номурун, түзмөктүн ID-син, чалуунун абалын жана байланышта чыккан номурду аныктоого жол берет."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"чалууларды тутум аркылуу өткөрүү"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Чалуунун сапатын жакшыртуу максатында колдонмого чалууларын тутум аркылуу өткөрүүгө уруксат берет."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"тутум аркылуу чалууларды көрүп, көзөмөлдөө."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Колдонмого түзмөктөгү аткарылып жаткан чалууларды көрүп, көзөмөлдөөгө уруксат берет. Буга чалуулардын саны жана абалы сыяктуу маалымат кирет."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"чалууну башка колдонмодон улантуу"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Башка колдонмодон аткарылган чалууну бул колдонмодо улантууга уруксат берүү."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"телефон номерлерин окуу"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Колдонмого Жакынкы аралыкта байланышуу (NFC) белгилери, карталары жана окугучтары менен байланышуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"экранды бөгөттөөнү өчүрүү"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Колдонмого экрандын бөгөттөөчү жана ага байланыштуу сырсөз коргоосун өчүрүү уруксатын берет. Мисалы, чалуу келгенде экрандын бөгөтүн алып салат, чалуу бүткөндө кайрадан орнотот."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"экранды бөгөттөө канчалык татаал экенин сурап, маалымат алуу"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Колдонмого экранды бөгөттөөнүн татаалдыгын (татаал, орточо, оңой же такыр жок) үйрөнүүгө мүмкүнчүлүк берет. Татаалдык деңгээли сырсөздүн узундугу жана экранды бөгөттөөнүн түрү боюнча айырмаланат. Колдонмо экранды бөгөттөөнү белгилүү деңгээлге тууралоону колдонуучуларга сунуштай да алат, бирок колдонуучулар ага көңүл бурбай койсо болот. Сырсөздү колдонмо билбеши үчүн, экранды бөгөттөө сырсөзүн кадимки текстте сактоого болбойт."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"биометрикалык аппаратты колдонуу"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Колдонмого аныктыгын текшерүү үчүн биометрикалык аппаратты пайдалануу мүмкүндүгүн берет"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"манжа изинин аппараттык камсыздоосун башкаруу"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Планшет жаңыртылууда…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Түзмөк жаңыртылууда…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон күйгүзүлүүдө…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android жүргүзүлүүдө…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Планшет күйгүзүлүүдө…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Түзмөк күйүгүзүлүүдө…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Сактагыч ыңгайлаштырылууда."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Бардык тармактарды көрүү үчүн басыңыз"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Туташуу"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Бардык тармактар"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> сунуштаган Wi‑Fi тармагына туташтырылды"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> сизге тармактарды сунуштай берсинби?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ооба"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Жок"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi автоматтык түрдө күйөт"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Байланыш сигналы күчтүү тармактарга жакындаганда"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Өзү кайра күйбөйт"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index e925d92..9356302 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ອະນຸຍາດໃຫ້ແອັບຯ ເຂົ້າເຖິງຄວາມສາມາດການໂທລະສັບຂອງອຸປະກອນ. ການກຳນົດສິດນີ້ເຮັດໃຫ້ແອັບຯສາມາດກວດສອບເບີໂທລະສັບ ແລະ ID ຂອງອຸປະກອນ, ບໍ່ວ່າການໂທຈະຍັງດຳເນີນຢູ່ ແລະເບີປາຍທາງເຊື່ອມຕໍ່ຢູ່ຫຼືບໍ່ກໍຕາມ."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ເຫັນ ແລະ ຄວບຄຸມການໂທຜ່ານລະບົບ."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ອະນຸຍາດໃຫ້ແອັບເຫັນ ແລະ ຄວບຄຸມການໂທທີ່ກຳລັງດຳເນີນຢູ່ອຸປະກອນ. ນີ້ຮວມເຖິງຂໍ້ມູນ ເຊັ່ນ: ເບີໂທສຳລັບການໂທ ແລະ ສະຖານະຂອງການໂທນຳ."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ສືບຕໍ່ການໂທຈາກແອັບອື່ນ"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ອະນຸຍາດໃຫ້ແອັບສືບຕໍ່ການໂທເຊິ່ງອາດຖືກເລີ່ມຕົ້ນໃນແອັບອື່ນ."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ອ່ານເບີໂທລະສັບ"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ອະນຸຍາດໃຫ້ແອັບຯຕິດຕໍ່ສື່ສານກັບປ້າຍກຳກັບ, ບັດ ແລະໂຕອ່ານຂອງການສື່ສານໄລຍະສັ້ນ (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ປິດການລັອກໜ້າຈໍ"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງປຸ່ມລັອກ ແລະລະບົບຄວາມປອດໄພຂອງລະຫັດຜ່ານທີ່ເຊື່ອມໂຍງກັນ. ໂຕຢ່າງ: ໂທລະສັບຈະປິດການເຮັດວຽກຂອງປຸ່ມລັອກເມື່ອມີສາຍໂທເຂົ້າ ຈາກນັ້ນຈຶ່ງເປີດໃຊ້ໄດ້ອີກເມື່ອວາງສາຍແລ້ວ."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ຮັບ ແລະ ຂໍຄວາມຊັບຊ້ອນການລັອກໜ້າຈໍ"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ອະນຸຍາດໃຫ້ແອັບສຶກສາລະດັບຄວາມຊັບຊ້ອນຂອງໜ້າຈໍລັອກ (ສູງ, ກາງ ຫຼື ບໍ່ມີ), ເຊິ່ງລະບຸຂອບເຂດຄວາມເປັນໄປໄດ້ຂອງຄວາມຍາວ ແລະ ປະເພດຂອງການລັອກໜ້າຈໍ. ແອັບນີ້ສາມາດແນະນຳຜູ້ໃຊ້ວ່າເຂົາເຈົ້າສາມາດອັບເດດໜ້າຈໍລັອກເປັນລະດັບໃດໜຶ່ງເປັນການສະເພາະໄດ້, ແຕ່ຜູ້ໃຊ້ສາມາດທີ່ຈະບໍ່ສົນໃຈ ຫຼື ເປີດໄປອັນອື່ນໄດ້. ກະລຸນາຮັບຊາບວ່າການລັອກໜ້າຈໍບໍ່ໄດ້ບັນທຶກໃນແບບຂໍ້ຄວາມທຳມະດາ, ດັ່ງນັ້ນແອັບຈະບໍ່ຮູ້ລະຫັດຜ່ານທີ່ແນ່ນອນ."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ໃຊ້ຮາດແວຊີວະມິຕິ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ອະນຸຍາດໃຫ້ແອັບນຳໃຊ້ຮາດແວຊີວະມິຕິສຳລັບການພິສູດຢືນຢັນ"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ຈັດການຮາດແວລາຍນີ້ວມື"</string>
@@ -1097,9 +1101,9 @@
<string name="add_contact_desc" msgid="4830217847004590345">"ເພີ່ມໃສ່ລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
<string name="view_calendar" msgid="979609872939597838">"ເບິ່ງ"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ເບິ່ງເວລາທີ່ເລືອກໃນປະຕິທິນ"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"ຕັ້ງເວລາ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ກຳນົດເວລາ"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ກຳນົດເວລາສຳລັບເວລາທີ່ເລືອກ"</string>
- <string name="view_flight" msgid="7691640491425680214">"ແທຣັກ"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ຕິດຕາມ"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ຕິດຕາມຖ້ຽວບິນທີ່ເລືອກ"</string>
<string name="translate" msgid="9218619809342576858">"ແປພາສາ"</string>
<string name="translate_desc" msgid="4502367770068777202">"ແປຂໍ້ຄວາມທີ່ເລືອກ"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ກຳລັງອັບເດດແທັບເລັດ…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ກຳລັງອັບເດດອຸປະກອນ…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ກຳລັງເລີ່ມໂທລະສັບ…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"ກຳລັງເລີ່ມລະບົບ Android …"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ກຳລັງເລີ່ມແທັບເລັດ…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ກຳລັງເລີ່ມອຸປະກອນ…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ການປັບບ່ອນເກັບຂໍ້ມູນໃຫ້ເໝາະສົມ."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ແຕະເພື່ອເບິ່ງເຄືອຂ່າຍທັງໝົດ"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"ເຊື່ອມຕໍ່"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ເຄືອຂ່າຍທັງໝົດ"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍ Wi‑Fi ທີ່ສະເໜີໂດຍ <xliff:g id="NAME">%s</xliff:g> ແລ້ວ"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"ທ່ານຕ້ອງການໃຫ້ <xliff:g id="NAME">%s</xliff:g> ສະເໜີເຄືອຂ່າຍໃຫ້ທ່ານບໍ່?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ແມ່ນ"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ບໍ່ແມ່ນ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ຈະມີການເປີດໃຊ້ Wi‑Fi ອັດຕະໂນມັດ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ເມື່ອທ່ານຢູ່ໃກ້ເຄືອຂ່າຍຄຸນນະພາບສູງທີ່ບັນທຶກໄວ້"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ບໍ່ຕ້ອງເປີດໃຊ້ຄືນໃໝ່"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ada983f..8870ebb 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Leidžiama programai pasiekti telefono funkcijas įrenginyje. Šis leidimas suteikia teisę programai nustatyti telefono numerį ir įrenginio ID, tai, ar skambutis aktyvus, ir skambučiu prijungtą nuotolinį numerį."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"nukreipti skambučius per sistemą"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Programai leidžiama nukreipti jos skambučius per sistemą siekiant pagerinti skambinimo paslaugas."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"matyti ir valdyti skambučius per sistemą."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Programai leidžiama matyti ir valdyti vykstančius skambučius įrenginyje. Tai apima tokią informaciją kaip skambučių telefono numeriai ir skambučių būsena."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"tęsti skambutį naudojant kitą programą"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Programai leidžiama tęsti skambutį, kuris buvo pradėtas naudojant kitą programą."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"skaityti telefonų numerius"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Leidžiama programai perduoti artimojo lauko ryšių technologijos (ALR) žymas, korteles ir skaitymo programas."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"išjungti ekrano užraktą"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Leidžiama programai neleisti klavišo užrakto ir visos susijusios slaptažodžio apsaugos. Pvz., telefonas neleidžia klavišo užrakto priimant gaunamąjį skambutį ir pakartotinai jį įgalina, kai skambutis baigiamas."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"gauti ir pateikti ekrano užrakto sudėtingumo užklausą"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Leidžiama programai sužinoti ekrano užrakto sudėtingumo lygį (aukštas, vidutinis, žemas arba nėra), nurodantį galimą ekrano užrakto trukmės diapazoną ir tipą. Be to, programa gali pasiūlyti naudotojams atnaujinti ekrano užraktą į tam tikrą lygį, bet naudotojai gali laisvai nepaisyti ir išeiti. Atminkite, kad ekrano užraktas nesaugomas kaip grynasis tekstas, todėl programa nežino tikslaus slaptažodžio."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"naudoti biometrinę aparatinę įrangą"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Leidžiama programai naudoti biometrinę aparatinę įrangą tapatybei nustatyti"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"tvarkyti piršto antspaudo aparatinę įrangą"</string>
@@ -1131,7 +1135,7 @@
<string name="map_desc" msgid="1836995341943772348">"Rasti vietą pasirinktu adresu"</string>
<string name="browse" msgid="1245903488306147205">"Atidaryti"</string>
<string name="browse_desc" msgid="8220976549618935044">"Atidaryti pasirinktą URL"</string>
- <string name="sms" msgid="4560537514610063430">"Pranešimas"</string>
+ <string name="sms" msgid="4560537514610063430">"Rašyti pranešimą"</string>
<string name="sms_desc" msgid="7526588350969638809">"Siųsti pranešimą pasirinktu telefono numeriu"</string>
<string name="add_contact" msgid="7867066569670597203">"Pridėti"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Pridėti prie kontaktų"</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Planšetinis kompiuteris atnaujinamas…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Įrenginys atnaujinamas…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonas paleidžiamas…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Paleidžiama „Android“…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Planšetinis kompiuteris paleidžiamas…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Įrenginys paleidžiamas…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizuojama saugykla."</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Palieskite, jei norite matyti visus tinklus"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Prisijungti"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Visi tinklai"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Prisijungta prie „<xliff:g id="NAME">%s</xliff:g>“ pasiūlyto „Wi‑Fi“ tinklo"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Ar norite leisti „<xliff:g id="NAME">%s</xliff:g>“ siūlyti jums tinklus?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Taip"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"„Wi‑Fi“ bus įjungtas automatiškai"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kai būsite netoli išsaugoto aukštos kokybės tinklo"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Neįjunkite vėl"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index dd4b125..5a1432b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -450,6 +450,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ļauj lietotnei piekļūt ierīces tālruņa funkcijām. Ar šo atļauju lietotne var noteikt tālruņa numuru un ierīču ID, zvana statusu un attālo numuru, ar ko ir izveidots savienojums, veicot zvanu."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"maršrutēt zvanus sistēmā"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ļauj lietotnei maršrutēt tās zvanus sistēmā, lai uzlabotu zvanīšanas pieredzi."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"skatīt un kontrolēt zvanus sistēmā."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ļauj lietotnei ierīcē skatīt un kontrolēt aktīvos zvanus. Šeit ir ietverta tāda informācija kā zvanu tālruņa numuri un zvanu statuss."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"turpināt zvanu no citas lietotnes"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ļauj lietotnei turpināt zvanu, kas tika sākts citā lietotnē."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lasīt tālruņa numurus"</string>
@@ -510,6 +512,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Ļauj lietotnei sazināties ar tuva darbības lauka sakaru (Near Field Communication — NFC) atzīmēm, kartēm un lasītājiem."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"atspējot ekrāna bloķēšanu"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ļauj lietotnei atspējot taustiņslēgu un visu saistīto paroļu drošību. Piemēram, tālrunis atspējo taustiņslēgu, saņemot ienākošu zvanu, un pēc zvana pabeigšanas atkārtoti iespējo taustiņslēgu."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"saņemt un pieprasīt informāciju par ekrāna bloķēšanas sarežģītību"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Atļauj lietotnei saglabāt informāciju par ekrāna bloķēšanas sarežģītības pakāpi (augsta, vidēja, zema, nav), kas apzīmē iespējamo garumu un ekrāna bloķēšanas veidus. Lietotnē lietotājiem var tikt rādīts arī ieteikums ekrāna bloķēšanai iestatīt citu līmeni, taču šo ieteikumu var ignorēt un aizvērt. Ņemiet vērā, ka ekrāna bloķēšanas informācija netiek glabāta vienkārša teksta formātā, tāpēc lietotnei nav piekļuves precīzai parolei."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"izmantot biometrisko datu aparatūru"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Atļauj lietotnei izmantot biometrisko datu aparatūru autentificēšanai"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"pārvaldīt pirkstu nospiedumu aparatūru"</string>
@@ -1105,7 +1109,7 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksta darbības"</string>
<string name="email" msgid="4560673117055050403">"E-pasts"</string>
<string name="email_desc" msgid="3638665569546416795">"Nosūtīt e-pasta ziņojumu uz atlasīto adresi"</string>
- <string name="dial" msgid="1253998302767701559">"Zvanīt"</string>
+ <string name="dial" msgid="1253998302767701559">"Zvans"</string>
<string name="dial_desc" msgid="6573723404985517250">"Zvanīt uz atlasīto tālruņa numuru"</string>
<string name="map" msgid="5441053548030107189">"Karte"</string>
<string name="map_desc" msgid="1836995341943772348">"Atrast atlasīto adresi"</string>
@@ -1201,6 +1205,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Notiek planšetdatora atjaunināšana…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Notiek ierīces atjaunināšana…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Notiek tālruņa palaišana…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Notiek Android palaišana…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Notiek planšetdatora palaišana…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Notiek ierīces palaišana…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Notiek krātuves optimizēšana."</string>
@@ -1261,6 +1266,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Pieskarieties, lai skatītu visus tīklus"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Izveidot savienojumu"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Visi tīkli"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Izveidots savienojums ar Wi-Fi tīklu, ko ieteica lietotne <xliff:g id="NAME">%s</xliff:g>."</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vai vēlaties, lai lietotne <xliff:g id="NAME">%s</xliff:g> iesaka tīklus?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Jā"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nē"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi tiks automātiski ieslēgts"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kad atrodaties saglabāta augstas kvalitātes tīkla tuvumā"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Neieslēgt atkārtoti"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 8b73b99..769ac43 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Овозможува апликацијата да пристапи кон карактеристиките на телефонот на уредот. Оваа дозвола овозможува апликацијата да ги утврди телефонскиот број и ID на уредот, дали повикот е активен и далечинскиот број поврзан со повикот."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"пренасочи повици преку системот"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозволете ѝ на апликацијата да ги пренасочи повиците преку системот за да го подобри искуството при јавувањето."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"гледање и контролирање повици преку системот."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Дозволува апликацијата да гледа и контролира тековни повици на уредот. Ова вклучува информации како телефонски броеви за повици и состојбата на повиците."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"продолжување повик од друга апликација"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Овозможува апликацијата да продолжи повик започнат на друга апликација."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"да чита телефонски броеви"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Дозволува апликацијата да комуницира со ознаки, картички и читачи за Комуникација при непосредна близина (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"оневозможи заклучување на екран"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Овозможува апликацијата да го оневозможи заклучувањето и каква било безбедност поврзана со лозинка. На пример, телефонот го оневозможува заклучувањето при прием на телефонски повик, а потоа повторно го овозможува заклучувањето кога повикот ќе заврши."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"добива и бара комплексност на заклучување екран"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ѝ дозволува на апликацијата да го научи нивото на комплексност за заклучувањето на екранот (високо, средно, ниско или нема), коешто ги означува можниот опсег на должина и типот на заклувањето на екранот. Апликацијата може да им дава предлози на корисниците да го ажурираат заклучувањето на екранот на одредено ниво, но корисниците можат да го игнорираат и да продолжат понатаму. Имајте предвид дека заклучувањето на екранот не се складира како обичен текст па апликацијата не ја знае точната лозинка."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"користи биометриски хардвер"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Дозволува апликацијата да користи биометриски хардвер за проверка"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"управувај хардвер за отпечатоци"</string>
@@ -1083,23 +1087,23 @@
<string name="deleteText" msgid="6979668428458199034">"Избриши"</string>
<string name="inputMethod" msgid="1653630062304567879">"Метод на внес"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Дејства со текст"</string>
- <string name="email" msgid="4560673117055050403">"E-пошта"</string>
+ <string name="email" msgid="4560673117055050403">"Испрати е-пошта"</string>
<string name="email_desc" msgid="3638665569546416795">"Испраќа е-порака на избраната адреса"</string>
<string name="dial" msgid="1253998302767701559">"Повикај"</string>
<string name="dial_desc" msgid="6573723404985517250">"Го повикува избраниот телефонски број"</string>
- <string name="map" msgid="5441053548030107189">"Карта"</string>
+ <string name="map" msgid="5441053548030107189">"Отвори карта"</string>
<string name="map_desc" msgid="1836995341943772348">"Лоцирај ја избраната адреса"</string>
<string name="browse" msgid="1245903488306147205">"Отвори"</string>
<string name="browse_desc" msgid="8220976549618935044">"Ја отвора избраната URL"</string>
- <string name="sms" msgid="4560537514610063430">"Порака"</string>
+ <string name="sms" msgid="4560537514610063430">"Испрати порака"</string>
<string name="sms_desc" msgid="7526588350969638809">"Испраќа порака на избраниот телефонски број"</string>
<string name="add_contact" msgid="7867066569670597203">"Додај"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Додава во контакти"</string>
<string name="view_calendar" msgid="979609872939597838">"Прикажи"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Го гледа избраното време во календарот"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Распоред"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Закажи"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Закажува настан за избраното време"</string>
- <string name="view_flight" msgid="7691640491425680214">"Песна"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Следи го"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Следи избран лет"</string>
<string name="translate" msgid="9218619809342576858">"Преведи"</string>
<string name="translate_desc" msgid="4502367770068777202">"Преведете го избраниот текст"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблетот се ажурира…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Уредот се ажурира…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Телефонот стартува…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android стартува…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблетот стартува…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Уредот стартува…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизирање на складирањето."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Допрете за да ги видите сите мрежи"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Поврзете се"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Сите мрежи"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Поврзано на Wi‑Fi мрежа обезбедена од <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Дали сакате да дозволите <xliff:g id="NAME">%s</xliff:g> да ви предлага мрежи?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ќе се вклучи автоматски"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Кога сте во близина на зачувана мрежа со висок квалитет"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не вклучувај повторно"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 8c451a2..620718d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ഉപകരണത്തിന്റെ ഫോൺ സവിശേഷതകൾ ആക്സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഈ അനുമതി ഫോൺ നമ്പർ, ഉപകരണ ഐഡികൾ, ഒരു കോൾ സജീവമാണോയെന്നത്, ഒരു കോൾ കണക്റ്റുചെയ്ത വിദൂര നമ്പർ എന്നിവ നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"കോളുകൾ സിസ്റ്റത്തിലൂടെ വിടുക"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"കോളിംഗ് അനുഭവം മെച്ചപ്പെടുത്തുന്നതിനായി തങ്ങളുടെ കോളുകൾ സിസ്റ്റത്തിലേയ്ക്ക് വഴിതിരിച്ചുവിടാൻ ആപ്പുകളെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"സിസ്റ്റത്തിലൂടെ കോളുകൾ കാണുകയും നിയന്ത്രിക്കുകയും ചെയ്യുക."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ഉപകരണത്തിൽ നിലവിലുള്ള കോളുകൾ കാണാനും നിയന്തിക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു. കോളുകൾക്കുള്ള നമ്പറുകളും അവയുടെ നിലയും പോലെയുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"മറ്റൊരു ആപ്പിൽ നിന്നുള്ള കോൾ തുടരുക"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"മറ്റൊരു ആപ്പിൽ ആരംഭിച്ച കോൾ തുടരാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ഫോൺ നമ്പറുകൾ റീഡുചെയ്യൽ"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"നിയർ ഫീൽഡ് കമ്മ്യൂണിക്കേഷൻ (NFC) ടാഗുകളുമായും കാർഡുകളുമായും റീഡറുകളുമായുള്ള ആശയവിനിമയത്തിന് അപ്ലിക്കേഷനുകളെ അനുവദിക്കുന്നു."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"നിങ്ങളുടെ സ്ക്രീൻ ലോക്ക് പ്രവർത്തനരഹിതമാക്കുക"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"കീലോക്കും ഏതെങ്കിലും അനുബന്ധ പാസ്വേഡ് സുരക്ഷയും പ്രവർത്തനരഹിതമാക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ഒരു ഇൻകമിംഗ് കോൾ സ്വീകരിക്കുമ്പോൾ ഫോൺ കീലോക്ക് പ്രവർത്തനരഹിതമാക്കുന്നു, കോൾ അവസാനിക്കുമ്പോൾ കീലോക്ക് വീണ്ടും പ്രവർത്തനക്ഷമമാകുന്നു."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"സ്ക്രീൻ ലോക്ക് സങ്കീർണ്ണത അഭ്യർത്ഥിച്ച്, നേടുക"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"സ്ക്രീൻ ലോക്കിന്റെ സാധ്യമായ നീളവും തരവും സൂചിപ്പിക്കുന്ന, അതിന്റെ സങ്കീർണ്ണതാ നില (ഉയർന്നത്, ഇടത്തരം, കുറഞ്ഞത് അല്ലെങ്കിൽ ഒന്നുമില്ല) മനസിലാക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. സ്ക്രീൻ ലോക്ക് ഒരു പ്രത്യേക തലത്തിലേക്ക് അപ്ഡേറ്റ് ചെയ്യുന്ന ഉപയോക്താക്കൾക്ക് നിർദ്ദേശിക്കാനും ആപ്പിനാവും, മാത്രമല്ല ഉപയോക്താക്കൾക്ക് എളുപ്പത്തിൽ അവഗണിക്കാനും മറ്റൊന്നിലേക്ക് നാവിഗേറ്റ് ചെയ്യാനുമാവും. പ്ലെയിൻടെക്സ്റ്റിൽ സ്ക്രീൻ ലോക്ക് സംഭരിക്കപ്പെട്ടിട്ടില്ലെന്ന കാര്യം ശ്രദ്ധിക്കുക, അതിനാൽ ആപ്പിന് കൃത്യമായ പാസ്വേഡ് അറിയില്ല."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ബയോമെട്രിക് ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"പരിശോധിച്ചുറപ്പിക്കുന്നതിനായി, ബയോമെട്രിക് ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ഫിംഗർപ്രിന്റ് ഹാർഡ്വെയർ നിയന്ത്രിക്കുക"</string>
@@ -1083,23 +1087,23 @@
<string name="deleteText" msgid="6979668428458199034">"ഇല്ലാതാക്കുക"</string>
<string name="inputMethod" msgid="1653630062304567879">"ടൈപ്പുചെയ്യൽ രീതി"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"ടെക്സ്റ്റ് പ്രവർത്തനങ്ങൾ"</string>
- <string name="email" msgid="4560673117055050403">"ഇമെയിൽ"</string>
+ <string name="email" msgid="4560673117055050403">"ഇമെയിൽ അയയ്ക്കൂക"</string>
<string name="email_desc" msgid="3638665569546416795">"തിരഞ്ഞെടുത്ത വിലാസത്തിലേക്ക് ഇമെയിൽ അയയ്ക്കുക"</string>
<string name="dial" msgid="1253998302767701559">"വിളിക്കുക"</string>
- <string name="dial_desc" msgid="6573723404985517250">"തിരഞ്ഞെടുത്ത നമ്പറിലേക്ക് കോൾ ചെയ്യുക"</string>
- <string name="map" msgid="5441053548030107189">"മാപ്പ്"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"തിരഞ്ഞെടുത്ത നമ്പറിലേക്ക് വിളിക്കുക"</string>
+ <string name="map" msgid="5441053548030107189">"മാപ്പ് തുറക്കുക"</string>
<string name="map_desc" msgid="1836995341943772348">"തിരഞ്ഞെടുത്ത വിലാസം കണ്ടെത്തുക"</string>
<string name="browse" msgid="1245903488306147205">"തുറക്കുക"</string>
- <string name="browse_desc" msgid="8220976549618935044">"തിരഞ്ഞെടുത്ത URL ഓപ്പൺ ചെയ്യുക"</string>
- <string name="sms" msgid="4560537514610063430">"സന്ദേശം"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"തിരഞ്ഞെടുത്ത URL തുറക്കുക"</string>
+ <string name="sms" msgid="4560537514610063430">"സന്ദേശം അയയ്ക്കുക"</string>
<string name="sms_desc" msgid="7526588350969638809">"തിരഞ്ഞെടുത്ത നമ്പറിലേക്ക് സന്ദേശം അയയ്ക്കുക"</string>
<string name="add_contact" msgid="7867066569670597203">"ചേർക്കുക"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"കോൺടാക്റ്റുകളിലേക്ക് ചേർക്കുക"</string>
<string name="view_calendar" msgid="979609872939597838">"കാണുക"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"കലണ്ടറിൽ തിരഞ്ഞെടുത്ത സമയം കാണുക"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"ഷെഡ്യൂള്"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ഷെഡ്യൂള് ചെയ്യുക"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"തിരഞ്ഞെടുത്ത സമയത്തേക്ക് ഇവന്റ് ഷെഡ്യൂൾ ചെയ്യുക"</string>
- <string name="view_flight" msgid="7691640491425680214">"ട്രാക്ക്"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ട്രാക്ക് ചെയ്യുക"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"തിരഞ്ഞെടുത്ത ഫ്ലൈറ്റ് ട്രാക്ക് ചെയ്യുക"</string>
<string name="translate" msgid="9218619809342576858">"വിവർത്തനം ചെയ്യുക"</string>
<string name="translate_desc" msgid="4502367770068777202">"തിരഞ്ഞെടുത്ത ടെക്സ്റ്റ് വിവർത്തനം ചെയ്യുക"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ടാബ്ലെറ്റ് അപ്ഡേറ്റ് ചെയ്യുന്നു…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ഉപകരണം അപ്ഡേറ്റ് ചെയ്യുന്നു…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ഫോൺ ആരംഭിക്കുന്നു…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ആരംഭിക്കുന്നു…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ടാബ്ലെറ്റ് ആരംഭിക്കുന്നു…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ഉപകരണം ആരംഭിക്കുന്നു…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"സ്റ്റോറേജ് ഒപ്റ്റിമൈസ് ചെയ്യുന്നു."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"എല്ലാ നെറ്റ്വർക്കുകളും കാണാൻ ടാപ്പുചെയ്യുക"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"കണക്റ്റുചെയ്യുക"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"എല്ലാ നെറ്റ്വർക്കുകളും"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> നിർദേശിച്ച വൈഫൈ നെറ്റ്വർക്കിൽ കണക്റ്റ് ചെയ്തു"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"നിങ്ങൾക്കായി <xliff:g id="NAME">%s</xliff:g>-നെ നെറ്റ്വർക്കുകൾ നിർദ്ദേശിക്കാൻ അനുവദിക്കണോ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ഉവ്വ്"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ഇല്ല"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"വൈഫൈ സ്വമേധയാ ഓണാകും"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"നിങ്ങൾ ഉയർന്ന നിലവാരമുള്ള സംരക്ഷിക്കപ്പെട്ട നെറ്റ്വർക്കിനരികിലെത്തുമ്പോൾ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"തിരികെ ഓണാക്കരുത്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 93f7e37..c0b23fc 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Апп нь төхөөрөмжийн утасны функцд хандах боломжтой. Энэ зөвшөөрөл нь апп-д утасны дугаар болон төхөөрөмжийн ID-г, дуудлага идэвхтэй эсэх, холын дугаар дуудлагаар холбогдсон байгаа эсэхийг тогтоох боломжийг олгоно,"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"системээр дамжуулах дуудлага"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дуудлагыг сайжруулахын тулд дуудлагаа системээр дамжуулах зөвшөөрлийг апп-д олгодог."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"систем дэх дуудлагыг харах болон хянах."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Аппад төхөөрөмж дээр хийж буй дуудлагыг харах болон хянахыг зөвшөөрдөг. Үүнд дуудлагын дугаар болон дуудлагын төлөв зэрэг мэдээллийг агуулдаг."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"дуудлагыг өөр аппаас үргэлжлүүлэх"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Аппад өөр аппад эхлүүлсэн дуудлагыг үргэлжлүүлэхийг зөвшөөрдөг."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"утасны дугаарыг унших"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Апп нь Ойролцоо Талбарын Холболт(NFC) таг, карт, болон уншигчтай холбогдох боломжтой."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"дэлгэцний түгжээг идэвхгүй болгох"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Апп нь түгжээ болон бусад холбоотой нууц үгийн аюулгүй байдлыг идэвхгүй болгох боломжтой. Жишээ нь бол утас нь дуудлага ирэх үед түгжээг идэвхгүй болгох ба дуудлага дуусахад буцаан идэвхтэй болгодог."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"дэлгэцийн түгжээний төвөгтэй байдлыг авах болон хүсэх"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Аппад дэлгэцийн түгжээний боломжит уртын хэмжээ болон төрлийг заадаг дэлгэцийн түгжээний төвөгтэй байдлын түвшнийг (өндөр, дундаж, бага эсвэл байхгүй) мэдэж авахыг зөвшөөрдөг. Түүнчлэн, апп хэрэглэгчдэд дэлгэцийн түгжээг тодорхой түвшинд шинэчлэхийг санал болгодог хэдий ч хэрэглэгч үүнийг чөлөөтэй үл хэрэгсэж, орхих боломжтой. Дэлгэцийн түгжээг ил бичвэрээр хадгалдаггүй тул апп тодорхой нууц үгийг мэддэггүй болохыг анхаарна уу."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"биометрийн техник хангамжийг ашиглах"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Aппад биометрийн техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"хурууны хээний програм хангамжийг удирдах"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблетыг шинэчилж байна…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Төхөөрөмжийг шинэчилж байна…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Утсыг эхлүүлж байна…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Андройд эхэлж байна..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблетыг эхлүүлж байна…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Төхөөрөмжийг эхлүүлж байна…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хадгалалтыг сайжруулж байна."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Бүх сүлжээг харахын тулд товшино уу"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Холбогдох"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Бүх сүлжээ"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>-с санал болгосон Wi-Fi сүлжээнд холбогдсон"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Та <xliff:g id="NAME">%s</xliff:g>-г танд сүлжээ санал болгохыг хүсэж байна уу?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Тийм"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Үгүй"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi автоматаар асна"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Таныг хадгалсан, өндөр чанартай сүлжээний ойролцоо байх үед"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Буцааж асаахгүй"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 902380b..f3d6b4b 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -317,18 +317,18 @@
<string name="permgrouplab_visual" msgid="8030190588123857921">"फोटो आणि व्हिडिओ"</string>
<string name="permgroupdesc_visual" msgid="3415827902566663546">"तुमचे फोटो आणि व्हिडिओ अॅक्सेस करा"</string>
<string name="permgrouprequest_visual" msgid="6907523945030290376">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला तुमचे फोटो आणि व्हिडिओ अॅक्सेस करू द्यायचे का?"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"विंडो सामग्री पुनर्प्राप्त करा"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"तुम्ही परस्परसंवाद करीत असलेल्या विंडोची सामग्री तपासा."</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"विंडोमधील आशय पुन्हा मिळवा"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"तुम्ही वापरत असलेल्या विंडोमधील आशय तपासा."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"स्पर्श करून अन्वेषण चालू करा"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"टॅप केलेले आयटम मोठ्याने बोलले जातील आणि जेश्चरचा वापर करून स्क्रीन एक्सप्लोर केली जाऊ शकते."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"तुम्ही टाइप करता त्या मजकुराचे निरीक्षण करा"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर आणि संकेतशब्द यासारखा वैयक्तिक डेटा समाविष्ट करते."</string>
- <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"डिस्प्ले मॅग्निफिकेशन नियंत्रित करा"</string>
- <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनाचा झूम स्तर आणि स्थिती निर्धारण नियंत्रित करा."</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"यामध्ये क्रेडिट कार्ड नंबर आणि पासवर्ड यासारखा वैयक्तिक डेटा समाविष्ट आहे."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"डिस्प्ले मोठा करण्यावर नियंत्रण ठेवा"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"डिस्प्लेची झूम पातळी आणि पोझिशनिंग नियंत्रित करा."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"जेश्चर करा"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"टॅप, स्वाइप, पिंच आणि इतर जेश्चर करू शकते."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"फिंगरप्रिंट जेश्चर"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"डिव्हाइसच्या फिंगरप्रिंट सेंसरवर केलेले जेश्चर कॅप्चर करू शकते."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"डिव्हाइसच्या फिंगरप्रिंट सेंन्सरवरील जेश्चर कॅप्चर करू शकते."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"डिव्हाइस च्या फोन वैशिष्ट्यांवर अॅक्सेस करण्यास अॅपला अनुमती देते. ही परवानगी कॉल अॅक्टिव्हेट असला किंवा नसला तरीही, फोन नंबर आणि डिव्हाइस आयडी आणि कॉलद्वारे कनेक्ट केलेला रिमोट नंबर निर्धारित करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"प्रणालीच्या माध्यमातून कॉल रूट करा"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कॉल करण्याचा अनुभव सुधारण्यासाठी अॅपला त्याचे कॉल प्रणालीच्या माध्यमातून रूट करू देते."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"सिस्टम वापरून कॉल पाहा आणि नियंत्रण ठेवा."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"डिव्हाइसवर येणार कॉल पाहण्यासाठी आणि नियंत्रित करण्यासाठी अॅपला अनुमती देते. यामध्ये कॉल करण्यासाठी कॉलचा नंबर आणि कॉलची स्थिती यासारख्या माहितीचा समावेश असतो."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"दुसऱ्या अॅपवरून कॉल करणे सुरू ठेवा"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"दुसऱ्या अॅपमध्ये सुरू झालेल्या कॉलला पुढे सुरू ठेवण्याची अॅपला अनुमती देते."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फोन नंबर वाचा"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"फील्ड जवळील कम्युनिकेशन (NFC) टॅग, कार्डे आणि वाचक यांच्यासह संवाद करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"तुमचे स्क्रीन लॉक अक्षम करा"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"स्क्रीन लॉक जटिलता मिळवा आणि त्यासाठी विनंती करा"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"अॅपला स्क्रीन लॉक जटिलता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य श्रेणी आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना हेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठराविक पातळीपर्यंत अपडेट करावे परंतु वापरकर्ते मुक्तपणे ते दुर्लक्षित करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेअर वापरा"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ऑथेंटिकेशनसाठी बायोमेट्रिक हार्डवेअरचा वापर करण्याची अॅपला अनुमती देते"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"फिंगरप्रिंट हार्डवेअर व्यवस्थापित करा"</string>
@@ -1110,7 +1114,7 @@
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> चालत आहे"</string>
<string name="app_running_notification_text" msgid="1197581823314971177">"अधिक माहितीसाठी किंवा अॅप थांबविण्यासाठी टॅप करा."</string>
- <string name="ok" msgid="5970060430562524910">"ठीक"</string>
+ <string name="ok" msgid="5970060430562524910">"ठीक आहे"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
<string name="yes" msgid="5362982303337969312">"ठीक"</string>
<string name="no" msgid="5141531044935541497">"रद्द करा"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"टॅबलेट अपडेट होत आहे…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"डिव्हाइस अपडेट होत आहे…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"फोन सुरू होत आहे…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android प्रारंभ करत आहे…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"टॅबलेट सुरू होत आहे…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"डिव्हाइस सुरू होत आहे…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"संचयन ऑप्टिमाइझ करत आहे."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"सर्व नेटवर्क पाहण्यासाठी टॅप करा"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"कनेक्ट करा"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"सर्व नेटवर्क"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ने सुचवलेल्या वाय-फाय नेटवर्कशी कनेक्ट केले"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"तुम्हाला <xliff:g id="NAME">%s</xliff:g> ने तुमच्यासाठी नेटवर्क सुचवायला हवे आहे का?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"होय"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"नाही"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाय-फाय आपोआप चालू होईल"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तुम्ही जेव्हा सेव्ह केलेल्या उच्च दर्जाच्या नेटवर्कजवळ असाल तेव्हा"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"पुन्हा चालू करू नका"</string>
@@ -1857,7 +1866,7 @@
<string name="language_selection_title" msgid="2680677278159281088">"एक भाषा जोडा"</string>
<string name="country_selection_title" msgid="2954859441620215513">"प्रदेश प्राधान्य"</string>
<string name="search_language_hint" msgid="7042102592055108574">"भाषा नाव टाइप करा"</string>
- <string name="language_picker_section_suggested" msgid="8414489646861640885">"सूचित केलेले"</string>
+ <string name="language_picker_section_suggested" msgid="8414489646861640885">"सुचवलेल्या भाषा"</string>
<string name="language_picker_section_all" msgid="3097279199511617537">"सर्व भाषा"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"सर्व प्रदेश"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"शोध"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 6492f48..0eb022a 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Membenarkan apl mengakses ciri telefon pada peranti. Kebenaran ini membolehkan apl menentukan nombor telefon dan ID peranti, sama ada panggilan aktif dan nombor jauh yang dihubungkan dengan panggilan."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"halakan panggilan menerusi sistem"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Membenarkan apl menghalakan panggilan menerusi sistem untuk meningkatkan pengalaman panggilan."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"lihat dan kawal panggilan melalui sistem."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Membenarkan apl melihat dan mengawal panggilan yang sedang berlangsung pada peranti. Ini termasuklah maklumat seperti nombor panggilan untuk panggilan dan keadaan panggilan tersebut."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"teruskan panggilan daripada apl lain"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Membenarkan apl meneruskan panggilan yang dimulakan dalam apl lain."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"baca nombor telefon"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Membenarkan apl berkomunikasi dengan teg, kad dan pembaca Komunikasi Medan Dekat (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"lumpuhkan kunci skrin anda"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Membenarkan apl melumpuhkan kunci kekunci dan sebarang keselamatan kata laluan yang berkaitan. Sebagai contoh, telefon melumpuhkan kunci kekunci apabila menerima panggilan telefon masuk kemudian mendayakan semula kunci kekunci apabila panggilan selesai."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"dapatkan dan minta kekompleksan kunci skrin"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Benarkan apl mengetahui tahap kekompleksan kunci skrin (tinggi, sederhana, rendah atau tiada) yang menunjukkan kemungkinan julat panjang dan jenis kunci skrin. Apl juga boleh mencadangkan kepada pengguna supaya mengemas kini kunci skrin pada tahap tertentu namun pengguna boleh mengabaikan dan menavigasi keluar dengan bebas. Sila ambil perhatian bahawa kunci skrin tidak disimpan dalam teks biasa, maka apl tidak mengetahui kata laluan yang tepat."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"gunakan perkakasan biometrik"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Membenarkan apl menggunakan perkakasan biometrik untuk pengesahan"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"urus perkakasan cap jari"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet sedang mengemas kini…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Peranti sedang mengemas kini…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon sedang dimulakan…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android sedang dimulakan…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet sedang dimulakan…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Peranti sedang dimulakan…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimumkan storan."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Ketik untuk melihat semua rangkaian"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Sambung"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Semua rangkaian"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Dihubungkan ke rangkaian Wi‑Fi yang dicadangkan oleh <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Adakah anda ingin <xliff:g id="NAME">%s</xliff:g> mencadangkan rangkaian kepada anda?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ya"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Tidak"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan dihidupkan secara automatik"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Apabila anda berada berdekatan dengan rangkaian disimpan yang berkualiti tinggi"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Jangan hidupkan kembali"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index a3da75e..1c5f0c7 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"အပလီကေးရှင်းအား ဖုန်းရဲ့ စွမ်းဆောင်ချက်များအား သုံးခွင့်ပြုပါ။ အပလီကေးရှင်းအနေဖြင့် ဖုန်းနံပါတ်၊ စက်နံပါတ်၊ ဖုန်းခေါ်နေမှု ရှိမရှိနှင့် တဖက်မှ ဖုန်းနံပါတ် များအား သိရှိနိုင်ပါသည်"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ခေါ်ဆိုမှုများကို စနစ်မှတစ်ဆင့် ဖြတ်သန်းခွင့်ပြုပါ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ခေါ်ဆိုမှု အတွေ့အကြုံ ပိုမိုကောင်းမွန်လာစေရန်အတွက် အက်ပ်၏ ခေါ်ဆိုမှုအား စနစ်မှတစ်ဆင့် ဖြတ်သန်းရန် ခွင့်ပြုပါသည်။"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"စနစ်မှတစ်ဆင့် ခေါ်ဆိုမှုများကို ကြည့်ရှုထိန်းချုပ်ပါ။"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"စက်ပစ္စည်းပေါ်ရှိ လက်ရှိခေါ်ဆိုမှုများကို အက်ပ်အား ကြည့်ရှုထိန်းချုပ်ခွင့်ပြုသည်။ ၎င်းတွင် ခေါ်ဆိုမှုနံပါတ်များနှင့် ခေါ်ဆိုမှုအခြေအနေများကဲ့သို့သော အခြေအနေများ ပါဝင်သည်။"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"အခြားအက်ပ်မှ ဖုန်းခေါ်ဆိုမှုကို ဆက်လက်ပြုလုပ်ပါ"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"အခြားအက်ပ်တွင် စတင်ထားသည့် ဖုန်းခေါ်ဆိုမှုကို ဆက်လက်ပြုလုပ်ရန် ဤအက်ပ်ကို ခွင့်ပြုသည်။"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ဖုန်းနံပါတ်များကို ဖတ်ရန်"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"အက်ပ်အား တာတို စက်ကွင်း ဆက်သွယ်ရေး (NFC) တဲဂ်များ၊ ကဒ်များ နှင့် ဖတ်ကြသူတို့နှင့် ဆက်သွယ်ပြောဆိုခွင့် ပြုသည်။"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ဖန်သားပြင် သော့ချခြင်းအား မလုပ်နိုင်အောင် ပိတ်ရန်"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"အပလီကေးရှင်းအား သော့ချခြင်းနှင့် သက်ဆိုင်ရာ စကားဝှက်သတ်မှတ်ခြင်းများအား မသုံးနိုင်အောင် ပိတ်ခြင်းကို ခွင့်ပြုရန်။ ဥပမာ ဖုန်းလာလျှင် သော့ပိတ်ခြင်း ပယ်ဖျက်ခြင်း၊ ဖုန်းပြောပြီးလျှင် သော့ကို အလိုအလျောက် ပြန်ပိတ်ခြင်း"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ဖုန်းမျက်နှာပြင်လော့ခ် ရှုပ်ထွေးမှုကို ရယူခြင်းနှင့် တောင်းခံခြင်း"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ဖုန်းမျက်နှာပြင်လော့ခ်၏ ရှုပ်ထွေးမှုအဆင့် (မြင့်၊ အလယ်အလတ်၊ နိမ့် သို့မဟုတ် မရှိ) အား လေ့လာရန် အက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက သတ်မှတ်ထားနိုင်သော ဖုန်းမျက်နှာပြင်လော့ခ်၏ စာလုံးရေနှင့် အမျိုးအစားကို ညွှန်ပြပေးသည်။ အသုံးပြုသူများအနေနှင့် ဖုန်းမျက်နှာပြင်လော့ခ်ကို အတိုင်းအတာတစ်ခုအထိ အဆင့်မြှင့်ရန် အက်ပ်က အကြံပြုနိုင်သည်။ သို့သော်လည်း အသုံးပြုသူများက ၎င်းကို ဂရုပြုမနေဘဲ လွတ်လပ်စွာ ကြည့်ရှုနိုင်ပါသည်။ ဖုန်းမျက်နှာပြင်လော့ခ်ကို စာသားအတိုင်း သိမ်းမထားသဖြင့် အက်ပ်သည် စကားဝှက်အစစ်ကို မသိနိုင်ကြောင်း သတိပြုပါ။"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ဇီဝဗေဒဆိုင်ရာ အချက်အလက်သုံး ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန်"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"အထောက်အထားစိစစ်ခြင်းအတွက် ဇီဝဗေဒဆိုင်ရာ သတင်းအချက်အလက်များသုံးသည့် ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန် အက်ပ်ကို ခွင့်ပြုသည်"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"လက်ဗွေရာပစ္စည်းကို စီမံမည်"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"တက်ဘလက်ကို အပ်ဒိတ်လုပ်နေသည်…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"စက်ပစ္စည်းကို အပ်ဒိတ်လုပ်နေသည်…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ဖုန်း စတင်နေသည်…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android စတင်နေ…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"တက်ဘလက် စတင်နေသည်…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"စက်ပစ္စည်း စတင်နေသည်…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"သိုလှောင်မှုအား ပြုပြင်ခြင်း။"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ကွန်ရက်အားလုံးကို ကြည့်ရန် တို့ပါ"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"ချိတ်ဆက်ရန်"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ကွန်ရက်အားလုံး"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> အကြံပြုထားသော Wi‑Fi ကွန် ရက်သို့ ချိတ်ဆက်ထားသည်"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"သင့်အတွက် ကွန်ရက်များ အကြံပြုရန် <xliff:g id="NAME">%s</xliff:g> ကို ခွင့်ပြုလိုပါသလား။"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ကို အလိုအလျောက် ပြန်ဖွင့်ပေးလိမ့်ပါမည်"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"သိမ်းဆည်းထားသည့် အရည်အသွေးမြင့်ကွန်ရက်များအနီးသို့ ရောက်ရှိသည့်အခါ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ပြန်မဖွင့်ပါနှင့်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 30cd600..233b98a 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Lar appen bruke enhetens telefonfunksjoner. Med denne tillatelsen kan appen finne telefonnummer og enhets-ID-er, registrere om en samtale pågår, og se det eksterne nummeret det opprettes en forbindelse med via oppringing."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"send anrop gjennom systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Lar appen sende anrop gjennom systemet for å forbedre anropsopplevelsen."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"se og kontrollere anrop i systemet."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Lar appen se og kontrollere anrop som pågår på enheten. Dette inkluderer informasjon som anropsnumre og tilstanden til anropene."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"fortsette et anrop fra en annen app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Lar appen fortsette et anrop som ble startet i en annen app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"les telefonnumre"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Lar appen kommunisere med etiketter, kort og lesere som benytter NFC-teknologi."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivere skjermlåsen"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Lar appen deaktivere tastelåsen og eventuell tilknyttet passordsikkerhet. Et eksempel er at telefonen deaktiverer tastelåsen når du mottar et innkommende anrop, og deretter aktiverer tastelåsen igjen når samtalen er ferdig."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"hente og be om kompleksitet for skjermlås"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lar appen lære skjermlåsens kompleksitetsnivå (høy, middels, lav eller ingen), som indikerer det mulige området for lengde og type skjermlås. Appen kan foreslå at brukeren oppdaterer skjermlåsen til et bestemt nivå, men brukere kan velge å ignorere dette og navigere bort. Vær oppmerksom på at skjermlåsen ikke er lagret klartekst, så appen kan ikke se det nøyaktige passordet."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"bruke biometrisk maskinvare"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Lar appen bruke biometrisk maskinvare til godkjenning"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrere fingeravtrykkmaskinvare"</string>
@@ -1083,21 +1087,21 @@
<string name="deleteText" msgid="6979668428458199034">"Slett"</string>
<string name="inputMethod" msgid="1653630062304567879">"Inndatametode"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
- <string name="email" msgid="4560673117055050403">"E-post"</string>
+ <string name="email" msgid="4560673117055050403">"Send e-post"</string>
<string name="email_desc" msgid="3638665569546416795">"Send e-post til den valgte adressen"</string>
<string name="dial" msgid="1253998302767701559">"Ring"</string>
<string name="dial_desc" msgid="6573723404985517250">"Ring det valgte telefonnummeret"</string>
- <string name="map" msgid="5441053548030107189">"Kart"</string>
+ <string name="map" msgid="5441053548030107189">"Se Kart"</string>
<string name="map_desc" msgid="1836995341943772348">"Finn den valgte adressen"</string>
<string name="browse" msgid="1245903488306147205">"Åpne"</string>
<string name="browse_desc" msgid="8220976549618935044">"Åpne den valgte nettadressen"</string>
- <string name="sms" msgid="4560537514610063430">"Melding"</string>
+ <string name="sms" msgid="4560537514610063430">"Send melding"</string>
<string name="sms_desc" msgid="7526588350969638809">"Send melding til det valgte telefonnummeret"</string>
<string name="add_contact" msgid="7867066569670597203">"Legg til"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Legg til i kontakter"</string>
<string name="view_calendar" msgid="979609872939597838">"Se"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Se det valgte klokkeslettet i kalenderen"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Oversikt"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Planlegg"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planlegg aktivitet for valgt klokkeslett"</string>
<string name="view_flight" msgid="7691640491425680214">"Spor"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Spor den valgte flyvningen"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Nettbrettet oppdateres …"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Enheten oppdateres …"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefonen starter …"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android starter …"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Nettbrettet starter …"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Enheten starter …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimaliser lagring."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Trykk for å se alle nettverkene"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Koble til"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle nettverk"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Koblet til et Wi-Fi-nettverk som ble foreslått av <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vil du la <xliff:g id="NAME">%s</xliff:g> foreslå nettverk for deg?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nei"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi slås på automatisk"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Når du er i nærheten av et lagret nettverk av høy kvalitet"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ikke slå på igjen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 188ce8b..0b2362a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"उपकरणको फोन विशेषताहरूको पहुँच गर्न अनुप्रयोगलाई अनुमति दिन्छ। यस अनुमतिले फोन नम्बर र उपकरणको IDs, कल सक्षम छ कि छैन र कलद्वारा जोडिएको टाढाको नम्बर निर्धारण गर्न अनुमति दिन्छ।"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"प्रणाली मार्फत कल गर्न दिनुहोस्"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कल गर्दाको अनुभवलाई सुधार्न यस अनुप्रयोगलाई प्रणाली मार्फत कलहरू गर्न अनुमति दिन्छ।"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"प्रणालीमार्फत कलहरू हेर्नुका साथै तिनीहरूलाई नियन्त्रण गर्नुहोस्।"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"अनुप्रयोगलाई यन्त्रमा जारी रहेका कलहरू हेर्नुका साथै तिनीहरूलाई गर्ने अनुमति दिनुहोस्। यसमा गरिएका कलहरूको सङ्ख्या र कलहरूको अवस्था जस्ता जानकारी समावेश हुन्छन्।"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्नुहोस्"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"यस अनुप्रयोगलाई अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्ने अनुमति दिन्छ।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फोन नम्बरहरू पढ्ने"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"अनुप्रयोगलाई नयाँ क्षेत्र संचार (NFC) ट्यागहरू, कार्डहरू र पाठकहरूसँग अन्तर्क्रिया गर्न अनुमति दिन्छ।"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"स्क्रिन लक असक्षम पार्नुहोस्"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"कुनै सम्बन्धित पासवर्ड सुरक्षा र किलकलाई असक्षम पार्न अनुप्रयोगलाई अनुमति दिन्छ। उदाहरणको लागि, अन्तर्गमन फोन कल प्राप्त गर्दा फोनले किलकलाई असक्षम पार्छ, त्यसपछि कल सकिएको बेला किलक पुनःसक्षम पार्छ।"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"स्क्रिन लकको जटिलतासम्बन्धी जानकारी प्राप्त गर्न र त्यसका लागि अनुरोध गर्न"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"यसले अनुप्रयोगलाई स्क्रिन लकको जटिलताको स्तर (उच्च, मध्यम, न्यून वा कुनै पनि होइन) थाहा पाउने अनुमति दिन्छ जसले स्क्रिन लकको लम्बाइको सम्भावित दायरा र त्यसको प्रकारलाई जनाउँछ। यसै गरी, यो अनुप्रयोगले प्रयोगकर्ताहरूलाई स्क्रिन लक अद्यावधिक गर्ने सुझाव पनि दिन सक्छ तर प्रयोगकर्ताहरू उक्त सुझावको बेवास्ता गरी बाहिर निस्कन सक्छन्। स्क्रिन लकलाई सादा पाठको ढाँचामा भण्डारण नगरिने हुँदा यो अनुप्रयोगले वास्तविक पासवर्ड थाहा पाउँदैन भन्ने कुरा याद राख्नुहोस्।"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेयर प्रयोग गर्नुहोस्"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"अनुप्रयोगलाई प्रमाणीकरणका लागि बायोमेट्रिक हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"औठाछाप हार्डवेयर व्यवस्थापन गर्नुहोस्"</string>
@@ -1083,28 +1087,28 @@
<string name="deleteText" msgid="6979668428458199034">"मेट्नुहोस्"</string>
<string name="inputMethod" msgid="1653630062304567879">"निवेश विधि"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"पाठ कार्यहरू"</string>
- <string name="email" msgid="4560673117055050403">"इमेल"</string>
+ <string name="email" msgid="4560673117055050403">"इमेल गर्नुहोस्"</string>
<string name="email_desc" msgid="3638665569546416795">"चयन गरिएको ठेगानामा इमेल पठाउनुहोस्"</string>
- <string name="dial" msgid="1253998302767701559">"कल"</string>
+ <string name="dial" msgid="1253998302767701559">"कल गर्नुहोस्"</string>
<string name="dial_desc" msgid="6573723404985517250">"चयन गरिएको फोन नम्बरमा कल गर्नुहोस्"</string>
- <string name="map" msgid="5441053548030107189">"नक्सा"</string>
+ <string name="map" msgid="5441053548030107189">"नक्सा अनुप्रयोग खोल्नुहोस्"</string>
<string name="map_desc" msgid="1836995341943772348">"चयन गरिएको ठेगाना पत्ता लगाउनुहोस्"</string>
<string name="browse" msgid="1245903488306147205">"खोल्नुहोस्"</string>
<string name="browse_desc" msgid="8220976549618935044">"चयन गरिएको URL खोल्नुहोस्"</string>
- <string name="sms" msgid="4560537514610063430">"सन्देश"</string>
+ <string name="sms" msgid="4560537514610063430">"सन्देश पठाउनुहोस्"</string>
<string name="sms_desc" msgid="7526588350969638809">"चयन गरिएको फोन नम्बरमा सन्देश पठाउनुहोस्"</string>
<string name="add_contact" msgid="7867066569670597203">"थप्नुहोस्"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"सम्पर्क सूचीमा थप्नुहोस्"</string>
<string name="view_calendar" msgid="979609872939597838">"हेर्नुहोस्"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"पात्रोमा चयन गरिएको समय हेर्नुहोस्"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"समयतालिका"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"समयतालिका बनाउनुहोस्"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"चयन गरिएको समयका लागि कार्यक्रमको समयतालिका बनाउनुहोस्"</string>
<string name="view_flight" msgid="7691640491425680214">"ट्र्याक गर्नुहोस्"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"चयन गरिएको उडान ट्रयाक गर्नुहोस्"</string>
<string name="translate" msgid="9218619809342576858">"अनुवाद गर्नुहोस्"</string>
- <string name="translate_desc" msgid="4502367770068777202">"चयन गरिएको पाठ अनुवाद गर्नुहोस्"</string>
- <string name="define" msgid="7394820043869954211">"परिभाषा गर्नुहोस्"</string>
- <string name="define_desc" msgid="7910883642444919726">"चयन गरिएको पाठ परिभाषा गर्नुहोस्"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"चयन गरिएको पाठको अनुवाद गर्नुहोस्"</string>
+ <string name="define" msgid="7394820043869954211">"परिभाषा दिनुहोस्"</string>
+ <string name="define_desc" msgid="7910883642444919726">"चयन गरिएको पाठको परिभाषा दिनुहोस्"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"भण्डारण ठाउँ सकिँदै छ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1187,6 +1191,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ट्याब्लेटको अद्यावधिक गरिँदै छ…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"यन्त्रको अद्यावधिक गरिँदै छ…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"फोन सुरु हुँदै छ…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android शुरू हुँदैछ..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ट्याब्लेट सुरु हुँदै छ…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"यन्त्र सुरु हुँदै छ…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"भण्डारण आफू अनुकूल गर्दै।"</string>
@@ -1245,6 +1250,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"सबै नेटवर्कहरू हेर्न ट्याप गर्नुहोस्"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"जडान गर्नुहोस्"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"सबै नेटवर्कहरू"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ले प्रस्ताव गरेको Wi‑Fi नेटवर्कमा जोडियो"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"तपाईं <xliff:g id="NAME">%s</xliff:g> लाई आफ्ना लागि नेटवर्कहरूको प्रस्ताव गर्ने अनुमति दिन चाहनुहुन्छ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"हो"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"होइन"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi स्वतः सक्रिय हुनेछ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तपाईं कुनै सुरक्षित गरिएको उच्च गुणस्तरीय नेटवर्कको नजिक हुनुभएको अवस्थामा"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"फेरि सक्रिय नगर्नुहोला"</string>
@@ -1743,7 +1752,7 @@
<string name="reason_service_unavailable" msgid="7824008732243903268">"प्रिन्ट सेवा सक्षम गरिएको छैन"</string>
<string name="print_service_installed_title" msgid="2246317169444081628">"<xliff:g id="NAME">%s</xliff:g> सेवा स्थापित भयो"</string>
<string name="print_service_installed_message" msgid="5897362931070459152">"सक्षम पार्न ट्याप गर्नुहोस्"</string>
- <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"प्रशासकको PIN प्रविष्ट गर्नुहोस्"</string>
+ <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"प्रशासकको PIN प्रविष्टि गर्नुहोस्"</string>
<string name="restr_pin_enter_pin" msgid="3395953421368476103">"PIN प्रविष्टि गर्नुहोस्"</string>
<string name="restr_pin_incorrect" msgid="8571512003955077924">"गलत"</string>
<string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"वर्तमान PIN"</string>
@@ -1905,7 +1914,7 @@
<string name="time_picker_hour_label" msgid="2979075098868106450">"घन्टा"</string>
<string name="time_picker_minute_label" msgid="5168864173796598399">"मिनेट"</string>
<string name="time_picker_header_text" msgid="143536825321922567">"समय सेट गर्नुहोस्"</string>
- <string name="time_picker_input_error" msgid="7574999942502513765">"मान्य समय प्रविष्ट गर्नुहोस्"</string>
+ <string name="time_picker_input_error" msgid="7574999942502513765">"मान्य समय प्रविष्टि गर्नुहोस्"</string>
<string name="time_picker_prompt_label" msgid="7588093983899966783">"समय टाइप गर्नुहोस्"</string>
<string name="time_picker_text_input_mode_description" msgid="4148166758173708199">"समय इनपुट गर्न पाठ इनपुट मोडमा स्विच गर्नुहोस्।"</string>
<string name="time_picker_radial_mode_description" msgid="4953403779779557198">"समय इनपुट गर्न घडी मोडमा स्विच गर्नुहोस्।"</string>
diff --git a/core/res/res/values-night/themes_permission_controller.xml b/core/res/res/values-night/themes_permission_controller.xml
index 0ad2bdc..a071927 100644
--- a/core/res/res/values-night/themes_permission_controller.xml
+++ b/core/res/res/values-night/themes_permission_controller.xml
@@ -28,8 +28,5 @@
<style name="Theme.DeviceDefault.PermissionGrant"
parent="@style/Theme.DeviceDefault.Dialog">
<item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
- <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
- <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
- <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
</style>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 2ff9fde..996b700 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Hiermee kan de app toegang krijgen tot de telefoonfuncties van het apparaat, Met deze toestemming kan de app het telefoonnummer en de apparaat-ID\'s bepalen, of een gesprek actief is, en het andere telefoonnummer waarmee wordt gebeld."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"gesprekken doorschakelen via het systeem"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Hiermee kan de app de bijbehorende gesprekken doorschakelen via het systeem om de belfunctionaliteit te verbeteren."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"gesprekken via het systeem bekijken en beheren"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Hiermee kan de app actieve gesprekken op het apparaat bekijken en beheren. Dit omvat informatie zoals nummers voor gesprekken en de status van de gesprekken."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"een gesprek voortzetten vanuit een andere app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Hiermee kan de app een gesprek voortzetten dat is gestart in een andere app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefoonnummers lezen"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Hiermee kan de app communiceren met NFC-tags (Near Field Communication), kaarten en lezers."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"je schermvergrendeling uitschakelen"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Hiermee kan de app de toetsenblokkering en bijbehorende wachtwoordbeveiliging uitschakelen. Zo kan de telefoon de toetsenblokkering uitschakelen wanneer je wordt gebeld en de toetsenblokkering weer inschakelen als het gesprek is beëindigd."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"complexiteit van schermbeveiliging ophalen en aanvragen"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Hiermee krijgt de app toestemming om het complexiteitsniveau van de schermvergrendeling te achterhalen (hoog, midden, laag of geen). Dat geeft een indicatie van het mogelijke lengtebereik en type van de schermvergrendeling. De app kan gebruikers ook voorstellen de schermvergrendeling naar een bepaald niveau te updaten, maar gebruikers kunnen dit altijd negeren en de app verlaten. De schermvergrendeling wordt niet opgeslagen in platte tekst, zodat de app het precieze wachtwoord niet weet."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"biometrische hardware gebruiken"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Hiermee kan de app biometrische hardware gebruiken voor verificatie"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"Vingerafdrukhardware beheren"</string>
@@ -1083,15 +1087,15 @@
<string name="deleteText" msgid="6979668428458199034">"Verwijderen"</string>
<string name="inputMethod" msgid="1653630062304567879">"Invoermethode"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string>
- <string name="email" msgid="4560673117055050403">"E-mail"</string>
+ <string name="email" msgid="4560673117055050403">"E-mailen"</string>
<string name="email_desc" msgid="3638665569546416795">"E-mail sturen aan geselecteerd e-mailadres"</string>
<string name="dial" msgid="1253998302767701559">"Bellen"</string>
<string name="dial_desc" msgid="6573723404985517250">"Geselecteerd telefoonnummer bellen"</string>
- <string name="map" msgid="5441053548030107189">"Kaart"</string>
+ <string name="map" msgid="5441053548030107189">"Kaart openen"</string>
<string name="map_desc" msgid="1836995341943772348">"Geselecteerd adres zoeken"</string>
<string name="browse" msgid="1245903488306147205">"Openen"</string>
<string name="browse_desc" msgid="8220976549618935044">"Geselecteerde URL openen"</string>
- <string name="sms" msgid="4560537514610063430">"Bericht"</string>
+ <string name="sms" msgid="4560537514610063430">"Bericht verzenden"</string>
<string name="sms_desc" msgid="7526588350969638809">"Sms sturen aan geselecteerd telefoonnummer"</string>
<string name="add_contact" msgid="7867066569670597203">"Toevoegen"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Toevoegen aan contacten"</string>
@@ -1100,7 +1104,7 @@
<string name="add_calendar_event" msgid="1953664627192056206">"Plannen"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Evenement plannen voor geselecteerde tijd"</string>
<string name="view_flight" msgid="7691640491425680214">"Volgen"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Geselecteerde vlucht tonen"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Geselecteerde vlucht volgen"</string>
<string name="translate" msgid="9218619809342576858">"Vertalen"</string>
<string name="translate_desc" msgid="4502367770068777202">"Geselecteerde tekst vertalen"</string>
<string name="define" msgid="7394820043869954211">"Definiëren"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet wordt geüpdatet…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Apparaat wordt geüpdatet…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefoon wordt gestart…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android wordt gestart…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet wordt gestart…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Apparaat wordt gestart…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Opslagruimte wordt geoptimaliseerd."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tik om alle netwerken te bekijken"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Verbinding maken"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alle netwerken"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Verbonden met wifi-netwerk dat is voorgesteld door <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Wil je dat <xliff:g id="NAME">%s</xliff:g> je netwerken kan voorstellen?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nee"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wifi wordt automatisch ingeschakeld"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wanneer je in de buurt van een opgeslagen netwerk van hoge kwaliteit bent"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Niet weer inschakelen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f1c7baf..27b7d5d 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ଆପ୍କୁ ଡିଭାଇସ୍ର ଫୋନ୍ ବୈଶିଷ୍ଟ୍ୟ ଆକ୍ସେସ୍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହି ଅନୁମତି ଆପ୍କୁ ଫୋନ୍ ନମ୍ବର୍ ଓ ଡିଭାଇସ୍ IDଗୁଡ଼ିକୁ ନିର୍ଦ୍ଧାରଣ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ, କଲ୍ ସକ୍ରିୟ ଥିଲେ ବି ଏବଂ କଲ୍ ଦ୍ୱାରା ସଂଯୋଗ ଥିବା ରିମୋଟ୍ ନମ୍ବର୍କୁ ମଧ୍ୟ।"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ସିଷ୍ଟମ୍ ଜରିଆରେ କଲ୍ର ମାର୍ଗ ବଦଳାଇପାରେ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"କଲ୍ କରିବାର ଅନୁଭୂତି ବଢ଼ାଇବାକୁ ସିଷ୍ଟମ୍ ଜରିଆରେ ଆପର କଲ୍ଗୁଡ଼ିକୁ ରୁଟ୍ କରିବାକୁ ଏହାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ସିଷ୍ଟମ୍ ମାଧ୍ୟମରେ କଲ୍ଗୁଡ଼ିକୁ ଦେଖିଥାଏ ଏବଂ ନିୟନ୍ତ୍ରଣ କରିଥାଏ।"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ଆପ୍କୁ ଡିଭାଇସ୍ରେ ଚାଲୁଥିବା କଲ୍ଗୁଡ଼ିକୁ ଦେଖିବାକୁ ଏବଂ ନିୟନ୍ତ୍ରଣ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏଥିରେ କଲ୍ଗୁଡ଼ିକ ପାଇଁ କଲ୍ ନମ୍ବର୍ ଏବଂ କଲ୍ଗୁଡ଼ିକର ସ୍ଥିତି ପରି ସୂଚନା ଅନ୍ତର୍ଭୁକ୍ତ ଅଛି।"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ଅନ୍ୟ ଏକ ଆପ୍ରୁ କଲ୍କୁ ଜାରି ରଖନ୍ତୁ"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ଅନ୍ୟ ଆପ୍ରେ ଆରମ୍ଭ ହୋଇଥିବା ଗୋଟିଏ କଲ୍କୁ ଜାରି ରଖିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ଫୋନ୍ ନମ୍ବର ପଢ଼େ"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ନିଅର୍ ଫିଲ୍ଡ କମ୍ୟୁନିକେସନ୍ନ (NFC) ଟାଗ୍, କାର୍ଡ ଓ ରିଡରଗୁଡ଼ିକ ସହ ଯୋଗାଯୋଗ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍ ଲକ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ଆପ୍କୁ କୀ\'ଲକ୍ କିମ୍ବା ସେଥିରେ ଥିବା କୌଣସି ପାସ୍ୱର୍ଡ ସୁରକ୍ଷାକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଅନୁମତି ଦିଏ, ଉଦାହରଣସ୍ୱରୂପ, ଇନ୍କମିଙ୍ଗ ଫୋନ୍ କଲ୍ ପ୍ରାପ୍ତ କରିବା ସମୟରେ ଫୋନ୍ଟି କୀ\'ଲକ୍କୁ ଅକ୍ଷମ କରିଦିଏ, ତା’ପରେ କଲ୍ ସମାପ୍ତ ହେବାପରେ ପୁଣି କୀ\'ଲକ୍କୁ ସକ୍ଷମ କରିଥାଏ।"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ସ୍କ୍ରିନ୍ ଲକ୍ର ଜଟିଳତା ପ୍ରାପ୍ତ କରନ୍ତୁ କିମ୍ବା ଅନୁରୋଧ କରନ୍ତୁ"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ସ୍କ୍ରିନ୍ ଲକ୍ର ଜଟିଳତା ସ୍ତର (ଉଚ୍ଚ, ମଧ୍ୟମ, ନିମ୍ନ କିମ୍ବା କିଛିନୁହେଁ), ଜାଣିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ, ଯାହା ସ୍କ୍ରିନ୍ ଲକ୍ର ସମ୍ଭାବ୍ୟ ପରିସୀମାର ଲମ୍ବ ଏବଂ ପ୍ରକାର ସୂଚୀତ କରେ। ଆପ୍ ଏହା ମଧ୍ୟ ଉପଯୋଗକର୍ତ୍ତାମାନଙ୍କୁ ପରାମର୍ଶ ଦେଇପାରେ ଯେ ସେମାନେ ସ୍କ୍ରିନ୍ ଲକ୍କୁ ଏକ ନିର୍ଦ୍ଧିଷ୍ଟ ସ୍ତର ପର୍ଯ୍ୟନ୍ତ ଅପ୍ଡେଟ୍ କରିପାରନ୍ତି, କିନ୍ତୁ ଉପଯୋଗକର୍ତ୍ତାମାନେ ନିଜ ଇଚ୍ଛାରେ ଏହାକୁ ଉପେକ୍ଷା ଏବଂ ନାଭିଗେଟ୍ କରିପାରିବେ। ଏହା ଧ୍ୟାନ ଦିଅନ୍ତୁ ଯେ ସ୍କ୍ରିନ୍ ଲକ୍ ସରଳ ଟେକ୍ସଟ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇନଥାଏ ଯେପରି ଆପ୍ ସଠିକ୍ ପାସ୍ୱର୍ଡ ଜାଣିପାରିନଥାଏ।"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ବାୟୋମେଟ୍ରିକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ସ୍ୱୀକୃତି ପାଇଁ ବାୟୋମେଟ୍ରିକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍ ପରିଚାଳନା କରନ୍ତୁ"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ଟାବଲେଟ୍ ଅପଡେଟ୍ ହେଉଛି…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ଡିଭାଇସ୍ ଅପଡେଟ୍ ହେଉଛି…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ଫୋନ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"ଆଣ୍ଡ୍ରଏଡ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ଟାବଲେଟ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ଡିଭାଇସ୍ ଆରମ୍ଭ ହେଉଛି…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ଷ୍ଟୋରେଜ୍ ବଢ଼ାଯାଉଛି"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ସମସ୍ତ ନେଟ୍ୱର୍କ ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"କନେକ୍ଟ କରନ୍ତୁ"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ସମସ୍ତ ନେଟ୍ୱର୍କ"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g>ଙ୍କ ଦ୍ୱାରା ପ୍ରସ୍ତାବିତ ୱାଇ-ଫାଇ ନେଟ୍ୱର୍କ ସହ ସଂଯୁକ୍ତ ହୋଇଛି"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"ଆପଣ କ’ଣ ଚାହୁଁଛନ୍ତି <xliff:g id="NAME">%s</xliff:g>, ଆପଣଙ୍କ ପାଇଁ ନେଟ୍ୱର୍କର ପ୍ରସ୍ତାବ ଦିଅନ୍ତୁ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ହଁ"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ନାହିଁ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ୱାଇ-ଫାଇ ସ୍ୱଚାଳିତ ଭାବେ ଅନ୍ ହେବ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ଆପଣ ଏକ ଉଚ୍ଚ-କ୍ୱାଲିଟୀ ବିଶିଷ୍ଟ ସେଭ୍ କରାଯାଇଥିବା ନେଟ୍ୱର୍କ ପାଖରେ ଥିବା ସମୟରେ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ପୁଣି ଅନ୍ କରନ୍ତୁ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index e3fe527..eacf006 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -318,7 +318,7 @@
<string name="permgroupdesc_visual" msgid="3415827902566663546">"ਤੁਹਾਡੀਆਂ ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਤੱਕ ਪਹੁੰਚ"</string>
<string name="permgrouprequest_visual" msgid="6907523945030290376">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਤੁਹਾਡੀਆਂ ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ਵਿੰਡੋ ਸਮੱਗਰੀ ਮੁੜ ਪ੍ਰਾਪਤ ਕਰਨਾ"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"ਇੱਕ ਵਿੰਡੋ ਦੀ ਸਮੱਗਰੀ ਦੀ ਜਾਂਚ ਕਰੋ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ ਅੰਤਰਕਿਰਿਆ ਕਰ ਰਹੇ ਹੋ"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"ਉਸ ਵਿੰਡੋ ਸਮੱਗਰੀ ਦੀ ਜਾਂਚ ਕਰੋ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ ਅੰਤਰਕਿਰਿਆ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"\'ਸਪੱਰਸ਼ ਰਾਹੀਂ ਪੜਚੋਲ ਕਰੋ\' ਚਾਲੂ ਕਰਨਾ"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"ਟੈਪ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਉੱਚੀ ਆਵਾਜ਼ ਵਿੱਚ ਬੋਲਿਆ ਜਾਵੇਗਾ ਅਤੇ ਸਕ੍ਰੀਨ ਦੀ ਸੰਕੇਤਾਂ ਦੀ ਵਰਤੋਂ ਨਾਲ ਪੜਚੋਲ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਟਾਈਪ ਕੀਤੀ ਲਿਖਤ ਦਾ ਨਿਰੀਖਣ ਕਰਨਾ"</string>
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ ਦੀਆਂ ਫ਼ੋਨ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਜਾਜ਼ਤ ਐਪ ਨੂੰ ਫ਼ੋਨ ਨੰਬਰ ਅਤੇ ਡੀਵਾਈਸ ਆਈ.ਡੀ. ਨਿਰਧਾਰਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ, ਇੱਕ ਕਾਲ ਕਿਰਿਆਸ਼ੀਲ ਹੈ ਜਾਂ ਨਹੀਂ ਅਤੇ ਰਿਮੋਟ ਨੰਬਰ ਇੱਕ ਕਾਲ ਨਾਲ ਕਨੈਕਟ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਰੂਟ ਕਰੋ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ਕਾਲ ਕਰਨ ਦੇ ਅਨੁਭਵ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ ਐਪ ਨੂੰ ਇਸਦੀਆਂ ਕਾਲਾਂ ਨੂੰ ਸਿਸਟਮ ਰਾਹੀਂ ਰੂਟ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਦੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ।"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ \'ਤੇ ਜਾਰੀ ਕਾਲਾਂ ਦੇਖਣ ਅਤੇ ਕੰਟਰੋਲ ਕਰਨ ਦਿਓ। ਇਸ ਵਿੱਚ ਕਾਲਾਂ ਦੇ ਨੰਬਰਾਂ ਅਤੇ ਉਹਨਾਂ ਦੀ ਸਥਿਤੀ ਬਾਰੇ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੈ।"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ਕਿਸੇ ਹੋਰ ਐਪ ਤੋਂ ਕਾਲ ਜਾਰੀ ਰੱਖੋ"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ਐਪ ਨੂੰ ਉਹ ਕਾਲ ਜਾਰੀ ਰੱਖਣ ਦਿਓ ਜਿਸਨੂੰ ਹੋਰ ਐਪ ਤੋਂ ਚਾਲੂ ਕੀਤਾ ਗਿਆ ਸੀ।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ਫ਼ੋਨ ਨੰਬਰ ਪੜ੍ਹੋ"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ (NFC) ਟੈਗਾਂ, ਕਾਰਡਾਂ ਅਤੇ ਰੀਡਰਾਂ ਨਾਲ ਸੰਚਾਰ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ਆਪਣਾ ਸਕ੍ਰੀਨ ਲਾਕ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ਐਪ ਨੂੰ ਕੀਲਾਕ ਅਤੇ ਕਿਸੇ ਵੀ ਸੰਬੰਧਿਤ ਪਾਸਵਰਡ ਸੁਰੱਖਿਆ ਨੂੰ ਬੰਦ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਫ਼ੋਨ ਇੱਕ ਇਨਕਮਿੰਗ ਫ਼ੋਨ ਕਾਲ ਪ੍ਰਾਪਤ ਕਰਨ ਵੇਲੇ ਬੰਦ ਕਰਦਾ ਹੈ, ਫਿਰ ਜਦੋਂ ਕਾਲ ਖਤਮ ਹੁੰਦੀ ਹੈ ਤਾਂ ਕੀਲਾਕ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਜਟਿਲਤਾ ਪ੍ਰਾਪਤ ਕਰੋ ਅਤੇ ਬੇਨਤੀ ਕਰੋ"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ਐਪ ਨੂੰ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਜਟਿਲਤਾ ਦੇ ਪੱਧਰ ਬਾਰੇ ਜਾਣਨ ਦਿਓ (ਉੱਚ, ਮੱਧਮ, ਘੱਟ ਜਾਂ ਕੋਈ ਨਹੀਂ), ਜੋ ਲੰਬਾਈ ਦੀ ਸੰਭਵ ਰੇਂਜ ਅਤੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਕਿਸਮ ਦਾ ਸੰਕੇਤ ਦਿੰਦਾ ਹੈ। ਐਪ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਇੱਕ ਖਾਸ ਪੱਧਰ ਤੱਕ ਸਕ੍ਰੀਨ ਲਾਕ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਸੁਝਾਅ ਵੀ ਦੇ ਸਕਦੀ ਹੈ ਪਰ ਵਰਤੋਂਕਾਰ ਇਸਨੂੰ ਅਣਡਿੱਠ ਕਰ ਸਕਦੇ ਹਨ। ਨੋਟ ਕਰੋ ਕਿ ਸਕ੍ਰੀਨ ਲਾਕ ਸਧਾਰਨ ਲਿਖਤ ਵਿੱਚ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਇਸ ਲਈ ਐਪ ਸਹੀ ਪਾਸਵਰਡ ਬਾਰੇ ਨਹੀਂ ਜਾਣਦੀ ਹੈ।"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦਾ ਹੈ"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ਟੈਬਲੈੱਟ ਅੱਪਡੇਟ ਹੋ ਰਿਹਾ ਹੈ…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"ਡੀਵਾਈਸ ਅੱਪਡੇਟ ਹੋ ਰਿਹਾ ਹੈ…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ਫ਼ੋਨ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ਟੈਬਲੈੱਟ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"ਡੀਵਾਈਸ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ਸਟੋਰੇਜ ਅਨੁਕੂਲ ਹੋ ਰਹੀ ਹੈ।"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ਸਾਰੇ ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"ਕਨੈਕਟ ਕਰੋ"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ਸਾਰੇ ਨੈੱਟਵਰਕ"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ਵੱਲੋਂ ਸੁਝਾਏ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"ਕੀ ਤੁਸੀਂ ਚਾਹੁੰਦੇ ਹੋ ਕਿ <xliff:g id="NAME">%s</xliff:g> ਤੁਹਾਨੂੰ ਨੈੱਟਵਰਕ ਸੁਝਾਏ?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ਹਾਂ"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ਨਹੀਂ"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ਵਾਈ‑ਫਾਈ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਚੱਲ ਪਵੇਗਾ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਰੱਖਿਅਤ ਕੀਤੇ ਉੱਚ-ਗੁਣਵੱਤਾ ਵਾਲੇ ਨੈੱਟਵਰਕ ਦੇ ਨੇੜੇ ਹੋਵੋ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ਵਾਪਸ ਚਾਲੂ ਨਾ ਕਰੋ"</string>
@@ -1513,7 +1522,7 @@
<string name="description_target_unlock_tablet" msgid="3833195335629795055">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਸਵਾਈਪ ਕਰੋ।"</string>
<string name="action_bar_home_description" msgid="5293600496601490216">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"ਉੱਪਰ ਜਾਓ"</string>
- <string name="action_menu_overflow_description" msgid="2295659037509008453">"ਹੋਰ ਚੋਣਾਂ"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"ਹੋਰ ਵਿਕਲਪ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="3570990907910199483">"ਅੰਦਰੂਨੀ ਸਾਂਝੀ ਕੀਤੀ ਸਟੋਰੇਜ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 85303ee..b2bb7b0 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Pozwala aplikacji na dostęp do funkcji telefonicznych urządzenia. Aplikacja z tym uprawnieniem może odczytać numer telefonu i identyfikator urządzenia, sprawdzić, czy połączenie jest aktywne, oraz poznać numer, z którym jest nawiązane połączenie."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"przekazywanie połączeń przez system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Zezwala aplikacji na przekazywanie połączeń przez system, by poprawić ich jakość."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"przeglądanie i kontrolowanie połączeń w systemie."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Zezwala aplikacji na przeglądanie i kontrolowanie trwających połączeń na urządzeniu. Dotyczy to informacji takich jak numery, z którymi nawiązane jest połączenie, oraz stan połączeń."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"kontynuuj połączenie w innej aplikacji"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Zezwala na kontynuowanie przez aplikację połączenia rozpoczętego w innej aplikacji."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"odczytywanie numerów telefonów"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Pozwala aplikacji na komunikowanie się z tagami, kartami i czytnikami NFC (Near Field Communication)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"wyłączanie blokady ekranu"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Pozwala aplikacji na wyłączenie blokady klawiatury i wszystkich związanych z tym haseł zabezpieczających. Na przykład telefon wyłącza blokadę klawiatury, gdy odbiera połączenie przychodzące, a następnie włącza ją ponownie po zakończeniu połączenia."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"uzyskiwanie informacji o stopniu trudności blokady ekranu i proszenie te informacje"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Zezwala aplikacji na poznanie stopnia złożoności blokady ekranu (wysoki, średni, niski lub brak), który wskazuje na możliwy zakres długości oraz typ blokady ekranu. Aplikacja może też zasugerować zaktualizowanie blokady ekranu pod kątem określonego stopnia trudności, ale użytkownik może to zignorować i zamknąć komunikat. Pamiętaj, że blokada ekranu nie jest zapisywana przy użyciu zwykłego tekstu, więc aplikacja nie pozna dokładnego hasła."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"używanie sprzętu biometrycznego"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Zezwala aplikacji na używanie sprzętu biometrycznego na potrzeby autoryzacji"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"zarządzanie czytnikiem linii papilarnych"</string>
@@ -1123,11 +1127,11 @@
<string name="deleteText" msgid="6979668428458199034">"Usuń"</string>
<string name="inputMethod" msgid="1653630062304567879">"Sposób wprowadzania tekstu"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string>
- <string name="email" msgid="4560673117055050403">"E-mail"</string>
+ <string name="email" msgid="4560673117055050403">"Wyślij e-maila"</string>
<string name="email_desc" msgid="3638665569546416795">"Wyślij e-maila na wybrany adres"</string>
<string name="dial" msgid="1253998302767701559">"Zadzwoń"</string>
<string name="dial_desc" msgid="6573723404985517250">"Zadzwoń pod wybrany numer telefonu"</string>
- <string name="map" msgid="5441053548030107189">"Mapa"</string>
+ <string name="map" msgid="5441053548030107189">"Otwórz mapę"</string>
<string name="map_desc" msgid="1836995341943772348">"Pokaż wybrany adres na mapie"</string>
<string name="browse" msgid="1245903488306147205">"Otwórz"</string>
<string name="browse_desc" msgid="8220976549618935044">"Otwórz wybrany adres URL"</string>
@@ -1139,7 +1143,7 @@
<string name="view_calendar_desc" msgid="5828320291870344584">"Wyświetl wybraną datę w kalendarzu"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Zaplanuj"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Zaplanuj wydarzenie na wybraną godzinę"</string>
- <string name="view_flight" msgid="7691640491425680214">"Utwór"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Śledź"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Śledź wybrany lot"</string>
<string name="translate" msgid="9218619809342576858">"Tłumacz"</string>
<string name="translate_desc" msgid="4502367770068777202">"Przetłumacz zaznaczony tekst"</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet się aktualizuje…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Urządzenie się aktualizuje…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon się uruchamia…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android się uruchamia…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet się uruchamia…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Urządzenie się uruchamia…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optymalizacja pamięci."</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Kliknij, by zobaczyć wszystkie sieci"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Połącz"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Wszystkie sieci"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Połączono z siecią Wi‑Fi zaproponowaną przez aplikację <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Czy chcesz zezwolić aplikacji <xliff:g id="NAME">%s</xliff:g> na proponowanie sieci?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Tak"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nie"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi włączy się automatycznie"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Gdy znajdziesz się w pobliżu zapisanej sieci o mocnym sygnale"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nie włączaj ponownie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 1cbcf0f..b1a1d39 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver e controlar chamadas pelo sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que o app veja e controle chamadas em andamento no dispositivo. Isso inclui informações como número e estado das chamadas."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada de outro app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que o app continue uma chamada que foi iniciada em outro app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de telefone"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permite que o app se comunique com leitores, cartões e etiqueta NFC (comunicação a curta distância)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"desativar o bloqueio de tela"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que o app desative o bloqueio de teclas e qualquer segurança por senha associada. Por exemplo, o telefone desativa o bloqueio de telas ao receber uma chamada e o reativa quando a chamada é finalizada."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"receber e solicitar complexidade de bloqueio de tela"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que o app saiba o nível de complexidade do bloqueio de tela (alto, médio, baixo ou nenhum), que indica o intervalo possível de comprimento e o tipo de bloqueio de tela. O app também pode sugerir a atualização do bloqueio de tela até um certo nível, mas os usuários podem ignorar a sugestão. O bloqueio de tela não é armazenado em texto simples, então o app não tem acesso à senha exata."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Usar hardware de biometria"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que o app use hardware de biometria para autenticação"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"gerenciar hardware de impressão digital"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"Excluir"</string>
<string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string>
- <string name="email" msgid="4560673117055050403">"E-mail"</string>
+ <string name="email" msgid="4560673117055050403">"Mandar e-mail"</string>
<string name="email_desc" msgid="3638665569546416795">"Enviar e-mail para endereço selecionado"</string>
<string name="dial" msgid="1253998302767701559">"Ligar"</string>
<string name="dial_desc" msgid="6573723404985517250">"Ligar para o número de telefone selecionado"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"O tablet está sendo atualizado…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo está sendo atualizado…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"O smartphone está iniciando…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"O Android está iniciando..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"O tablet está iniciando…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo está iniciando…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Otimizando o armazenamento."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toque para ver todas as redes"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectar"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectado à rede Wi-Fi sugerida por <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Permitir que o app <xliff:g id="NAME">%s</xliff:g> faça sugestões de rede?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sim"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Não"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6ad4ec3..ec4cf8c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que a aplicação aceda às funcionalidades de telefone do dispositivo. Esta autorização permite que a aplicação determine o número de telefone e IDs do dispositivo, se alguma chamada está ativa e qual o número remoto ligado por uma chamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas através do sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que a aplicação encaminhe as respetivas chamadas através do sistema de modo a melhorar a experiência da chamada."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver e controlar chamadas através do sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite à aplicação ver e controlar as chamadas em curso no dispositivo. Isto inclui informações como números de telefone das chamadas e o estado das mesmas."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada a partir de outra aplicação"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite à aplicação continuar uma chamada iniciada noutra aplicação."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler os números de telefone"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permite que a aplicação comunique com etiquetas, cartões e leitores Near Field Communication (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"desativar o bloqueio do ecrã"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que a aplicação desative o bloqueio de teclas e qualquer segurança por palavra-passe associada. Por exemplo, o telemóvel desativa o bloqueio de teclas quando recebe uma chamada e reativa o bloqueio de teclas ao terminar a chamada."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"obter e solicitar a complexidade do bloqueio de ecrã"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que a aplicação aprenda o nível de complexidade do bloqueio de ecrã (elevado, médio, baixo ou nenhum), que indica o intervalo de comprimento e o tipo de bloqueio de ecrã possíveis. A aplicação também pode sugerir aos utilizadores que atualizem o bloqueio de ecrã para um determinado nível, mas estes podem ignorar livremente a sugestão e continuar a navegação. Tenha em atenção que o bloqueio de ecrã não é armazenado em texto simples, pelo que a aplicação desconhece a palavra-passe exata."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Utilizar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que a aplicação utilize hardware biométrico para autenticação."</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"gerir o hardware de impressão digital"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"O tablet está a atualizar…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo está a atualizar…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"O telemóvel está a iniciar…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"O Android está a iniciar…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"O tablet está a iniciar…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo está a iniciar…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"A otimizar o armazenamento."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toque para ver todas as redes"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Ligar"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Ligação a rede Wi‑Fi proposta por <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Pretende permitir que <xliff:g id="NAME">%s</xliff:g> lhe proponha redes?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sim"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Não"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando estiver próximo de uma rede de alta qualidade guardada."</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não reativar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 1cbcf0f..b1a1d39 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ver e controlar chamadas pelo sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite que o app veja e controle chamadas em andamento no dispositivo. Isso inclui informações como número e estado das chamadas."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada de outro app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que o app continue uma chamada que foi iniciada em outro app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de telefone"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permite que o app se comunique com leitores, cartões e etiqueta NFC (comunicação a curta distância)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"desativar o bloqueio de tela"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite que o app desative o bloqueio de teclas e qualquer segurança por senha associada. Por exemplo, o telefone desativa o bloqueio de telas ao receber uma chamada e o reativa quando a chamada é finalizada."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"receber e solicitar complexidade de bloqueio de tela"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite que o app saiba o nível de complexidade do bloqueio de tela (alto, médio, baixo ou nenhum), que indica o intervalo possível de comprimento e o tipo de bloqueio de tela. O app também pode sugerir a atualização do bloqueio de tela até um certo nível, mas os usuários podem ignorar a sugestão. O bloqueio de tela não é armazenado em texto simples, então o app não tem acesso à senha exata."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Usar hardware de biometria"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite que o app use hardware de biometria para autenticação"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"gerenciar hardware de impressão digital"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"Excluir"</string>
<string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string>
- <string name="email" msgid="4560673117055050403">"E-mail"</string>
+ <string name="email" msgid="4560673117055050403">"Mandar e-mail"</string>
<string name="email_desc" msgid="3638665569546416795">"Enviar e-mail para endereço selecionado"</string>
<string name="dial" msgid="1253998302767701559">"Ligar"</string>
<string name="dial_desc" msgid="6573723404985517250">"Ligar para o número de telefone selecionado"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"O tablet está sendo atualizado…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"O dispositivo está sendo atualizado…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"O smartphone está iniciando…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"O Android está iniciando..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"O tablet está iniciando…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"O dispositivo está iniciando…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Otimizando o armazenamento."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Toque para ver todas as redes"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectar"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Todas as redes"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectado à rede Wi-Fi sugerida por <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Permitir que o app <xliff:g id="NAME">%s</xliff:g> faça sugestões de rede?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Sim"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Não"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index dca0510..5b36b9a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -450,6 +450,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcțiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"să direcționeze apelurile prin intermediul sistemului"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permiteți aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Vedeți și controlați apelurile prin intermediul sistemului."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Permite aplicației să vadă și să controleze apelurile în desfășurare pe dispozitiv. Aceasta include informații ca numerele pentru apeluri și starea apelurilor."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"să continue un apel dintr-o altă aplicație"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite aplicației să continue un apel care a fost inițiat dintr-o altă aplicație."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"să citească numerele de telefon"</string>
@@ -510,6 +512,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Permite aplicației să comunice cu etichetele, cardurile și cititoarele NFC (Near Field Communication)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"dezactivează blocarea ecranului"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Permite aplicației să dezactiveze blocarea tastelor și orice modalitate asociată de securizare prin parolă. De exemplu, telefonul dezactivează blocarea tastelor când se primește un apel telefonic și reactivează blocarea tastelor la terminarea apelului."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"primește și solicită complexitatea blocării ecranului"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Permite aplicației să învețe nivelul de complexitate al blocării ecranului (ridicat, mediu, scăzut sau fără) ce indică intervalul posibil de lungime a parolei și tipul de blocare a ecranului. Aplicația le poate sugera utilizatorilor să își actualizeze blocarea ecranului la un anumit nivel, dar utilizatorii pot ignora sugestia și pot naviga în continuare. Rețineți că blocarea ecranului nu este stocată ca text simplu, astfel încât aplicația să nu cunoască parola exactă."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"utilizați hardware biometric"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permite aplicației să folosească hardware biometric pentru autentificare"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"gestionează hardware-ul pentru amprentă"</string>
@@ -1104,21 +1108,21 @@
<string name="inputMethod" msgid="1653630062304567879">"Metodă de intrare"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acțiuni pentru text"</string>
<string name="email" msgid="4560673117055050403">"E-mail"</string>
- <string name="email_desc" msgid="3638665569546416795">"Trimiteți e-mail la adresa selectată"</string>
- <string name="dial" msgid="1253998302767701559">"Apelați"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Apelați numărul de telefon selectat"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Trimiteți un e-mail la adresa selectată"</string>
+ <string name="dial" msgid="1253998302767701559">"Sunați"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Sunați la numărul de telefon selectat"</string>
<string name="map" msgid="5441053548030107189">"Hartă"</string>
<string name="map_desc" msgid="1836995341943772348">"Localizați adresa selectată"</string>
<string name="browse" msgid="1245903488306147205">"Deschideți"</string>
<string name="browse_desc" msgid="8220976549618935044">"Deschideți adresa URL selectată"</string>
- <string name="sms" msgid="4560537514610063430">"Mesaj"</string>
+ <string name="sms" msgid="4560537514610063430">"Trimiteți mesaj"</string>
<string name="sms_desc" msgid="7526588350969638809">"Trimiteți un mesaj la numărul de telefon selectat"</string>
<string name="add_contact" msgid="7867066569670597203">"Adăugați"</string>
- <string name="add_contact_desc" msgid="4830217847004590345">"Adăugați la persoanele din Agendă"</string>
+ <string name="add_contact_desc" msgid="4830217847004590345">"Adăugați în agendă"</string>
<string name="view_calendar" msgid="979609872939597838">"Afișați"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Afișați ora selectată în calendar"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Program"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programați evenimentul pentru momentul selectat"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Afișați data selectată în calendar"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Programați"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programați evenimentul pentru data selectată"</string>
<string name="view_flight" msgid="7691640491425680214">"Urmăriți"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Urmăriți zborul selectat"</string>
<string name="translate" msgid="9218619809342576858">"Traduceți"</string>
@@ -1201,6 +1205,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Se actualizează tableta…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Se actualizează dispozitivul.…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Pornește telefonul…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android pornește..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Pornește tableta…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Pornește dispozitivul…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Se optimizează stocarea."</string>
@@ -1261,6 +1266,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Atingeți pentru a vedea toate rețelele"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Conectați-vă"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Toate rețelele"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Conectat la rețeaua Wi-Fi propusă de <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Permiteți aplicației <xliff:g id="NAME">%s</xliff:g> să vă sugereze rețele?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nu"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se va activa automat"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Când vă aflați lângă o rețea salvată, de înaltă calitate"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nu reactivați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8278c28..ac7908c0 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Приложение получит доступ к функциям телефона на устройстве. Кроме того, оно сможет определять номера телефонов и серийные номера моделей, состояние активности вызова, а также удаленные номера, с которыми установлено соединение."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"перенаправлять звонки в системе"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Приложение сможет перенаправлять звонки в системе с целью улучшения качества связи."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Управление вызовами через систему"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Приложение сможет управлять текущими вызовами на устройстве, а также получит доступ к сведениям о них, в том числе к номерам телефонов и статусам звонков."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"Продолжить вызов, начатый в другом приложении"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Разрешает приложению продолжить вызов, начатый в другом приложении."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"чтение номеров телефонов"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Приложение сможет обмениваться данными с NFC-метками, картами и устройствами считывания, используя NFC."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"Отключение функции блокировки экрана"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Приложение сможет отключать блокировку экрана и другие функции защиты. Например, блокировка экрана будет отключаться при получении входящего вызова и включаться после завершения разговора."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Доступ к информации об уровне сложности блокировки экрана и предоставление рекомендаций по его повышению"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Приложение получит доступ к сведениям об уровне сложности блокировки экрана (высокий, средний, низкий или не задан), в том числе о типе блокировки и длине пароля. Кроме того, оно сможет предлагать пользователям повысить уровень сложности блокировки. Эти рекомендации необязательны. Обратите внимание, что пароль не хранится в виде открытого текста и недоступен приложению."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Использование биометрического оборудования"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Приложение сможет использовать биометрическое оборудование для аутентификации"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"управление сканером отпечатков"</string>
@@ -1123,7 +1127,7 @@
<string name="deleteText" msgid="6979668428458199034">"Удалить"</string>
<string name="inputMethod" msgid="1653630062304567879">"Способ ввода"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Операции с текстом"</string>
- <string name="email" msgid="4560673117055050403">"Письмо"</string>
+ <string name="email" msgid="4560673117055050403">"Написать письмо"</string>
<string name="email_desc" msgid="3638665569546416795">"Отправить письмо выбранному адресату"</string>
<string name="dial" msgid="1253998302767701559">"Позвонить"</string>
<string name="dial_desc" msgid="6573723404985517250">"Позвонить по выбранному номеру"</string>
@@ -1137,7 +1141,7 @@
<string name="add_contact_desc" msgid="4830217847004590345">"Добавить в контакты"</string>
<string name="view_calendar" msgid="979609872939597838">"Открыть"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Посмотреть выбранный день в календаре"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Расписание"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Запланировать"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Запланировать событие на выбранный день"</string>
<string name="view_flight" msgid="7691640491425680214">"Отследить"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Отслеживать выбранный рейс"</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Обновление планшета…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Обновление устройства…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Запуск телефона…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Запуск Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Запуск планшета…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Запуск устройства…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизация хранилища…"</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Нажмите, чтобы увидеть список сетей"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Подключиться"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Все сети"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Установлено подключение к сети Wi‑Fi, найденной приложением \'<xliff:g id="NAME">%s</xliff:g>\'"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Разрешить приложению \'<xliff:g id="NAME">%s</xliff:g>\' предлагать вам сети?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Нет"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi включится автоматически"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Когда вы будете в зоне действия сохраненной сети с хорошим сигналом."</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не включать снова"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 7a43ecd..6486f3a 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"උපාංගයේ දුරකථන විශේෂාංග වෙත ප්රවේශයට යෙදුමට ඉඩ දෙයි. ඇමතුම සක්රිය වුවත්, සහ ඇමතුමකින් දුරස්ථ අංකය සම්බන්ධ වුවත් දුරකථන අංකය සහ උපාංග ID හඳුනා ගැනීමට මෙම අවසරය යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"පද්ධතිය හරහා ඇමතුම් මාර්ගගත කරන්න"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ඇමතුම් අත්දැකීම වැඩිදියුණු කිරීම සඳහා යෙදුමට පද්ධතිය හරහා එහි ඇමතුම් මාර්ගගත කිරීමට ඉඩ දෙයි."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"පද්ධතිය හරහා ඇමතුම් බලන්න සහ පාලනය කරන්න."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"යෙදුමට උපාංගයෙහි පවතින ඇමතුම් බැලීමට සහ පාලනය කිරීමට ඉඩ දෙයි මෙහි ඇමතුම් සඳහා ඇමතුම් අංක සහ ඇමතුම්වල තත්ත්වය වැනි තොරතුරු ඇතුළත් වේ."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"වෙනත් යෙදුමක් වෙතින් වන ඇමතුමක් දිගටම කරගෙන යන්න"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"වෙනත් යෙදුමක ආරම්භ කරන ලද ඇමතුමක් දිගටම කරගෙන යාමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"දුරකථන අංක කියවන්න"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ආසන්න ක්ෂේත්ර සන්නිවේදන (NFC) ටැග්, පත්, සහ කියවන්නන් සමඟ සන්නිවේදනය කිරීමට යෙදුමට අවසර දෙන්න."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ඔබගේ තිරයේ අගුල අබල කරන්න"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"යතුරු අගුල සහ ඕනෑම සම්බන්ධිත මුරපද ආරක්ෂාවක් අබල කිරීමට යෙදුමට අවසර දෙන්න. මෙහි උදාහරණයක් වන්නේ පැමිණෙන ඇමතුමක් ලැබෙද්දී, දුරකථනය අක්රිය වන අතර ඇමතුම අවසාන වන විට යතුරු අගුල නැවත සක්රිය වෙයි."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"තිර අගුලු සංකිර්ණය ලබා ගන්න සහ ඉල්ලන්න"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"තිර අගුලෙහි තිබිය හැකි දිගෙහි පරාසය හා වර්ගයේ පිළිබිඹු කරන, තිර අගුලු සංකීර්ණතා මට්ටම (ඉහළ, මධ්යම, අඩු හෝ රහිත) දැන ගැනීමට යෙදුමට ඉඩ දෙයි. තිරයේ අගුල නිශ්චිත මට්ටමක් වෙත යාවත්කාලීන කරන බවද මෙම යෙදුම පරිශීලකයින්ට යෝජනා කළ හැකිය. නමුත් පරිශීලකයන්ට නිදහසේ නොසලකා හැර සංචාලනය කළ හැකිය. තිර අලුල සරල පෙළ තුළ ගබඩා කර නැති බව සලකන්න එම නිසා යෙදුම නිවැරදි මුරපදය නොදනී."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ජීවමිතික දෘඪාංග භාවිත කරන්න"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"සත්යාපනය සඳහා ජීවමිතික දෘඪාංග භාවිත කිරීමට යෙදුමට ඉඩ දෙයි"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ඇඟිලි සලකුණු දෘඩාංග කළමනාකරණය කිරීම."</string>
@@ -1183,6 +1187,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ටැබ්ලට් පරිගණකය යාවත්කාලීන කරමින්..."</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"උපාංගය යාවත්කාලීන කරමින්..."</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"දුරකථනය ආරම්භ කරමින්…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ආරම්භ කරමින්…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ටැබ්ලට් පරිගණකය ආරම්භ කරමින්…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"උපාංගය ආරම්භ කරමින්…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"ආචයනය ප්රශස්තිකරණය කිරීම."</string>
@@ -1241,6 +1246,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"සියලු ජාල බැලීමට තට්ටු කරන්න"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"සම්බන්ධ කරන්න"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"සියලු ජාල"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> විසින් යෝජිත Wi-Fi ජාලයට සම්බන්ධ වුණි"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g>ඔබට ඔබ සඳහා ජාල යෝජනා කිරීමට අවශ්යද?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ඔව්"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"නැත"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ස්වයංක්රියව ක්රියාත්මක වනු ඇත"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ඔබ උසස් තත්ත්වයේ සුරැකි ජාලයක් අවට සිටින විට"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"නැවත ක්රියාත්මක නොකරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 7e158c2..66d6ef6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Umožňuje aplikácii pristupovať k telefónnym funkciám zariadenia. Aplikácia s týmto povolením môže určiť telefónne číslo a ID zariadenia, či práve prebieha hovor, a vzdialené číslo, s ktorým je prostredníctvom hovoru nadviazané spojenie."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"presmerovanie hovorov cez systém"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Umožňuje aplikácii presmerovať hovory cez systém na účely zlepšenia kvality hovorov."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"zobrazenie a kontrola hovorov prostredníctvom systému."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Umožňuje aplikácii zobrazovať a kontrolovať prebiehajúce hovory v zariadení. Táto možnosť zahŕňa údaje, akými sú napríklad čísla jednotlivých hovorov alebo stav hovorov."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"pokračovať v hovore z inej aplikácie"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Umožňuje aplikácii pokračovať v hovore začatom v inej aplikácii."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čítanie telefónnych čísel"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Umožňuje aplikácii komunikovať so značkami, kartami a čítačkami s podporou technológie NFC."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"deaktivácia zámky obrazovky"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Umožňuje aplikácii zakázať uzamknutie klávesnice a akékoľvek súvisiace zabezpečenie heslom. Príkladom je zakázanie uzamknutia klávesnice pri prichádzajúcom telefonickom hovore a jeho opätovné povolenie po skončení hovoru."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"získavať a vyžadovať zložitosť zámky obrazovky"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Umožňuje aplikácii zapamätať si úroveň zložitosti zámky obrazovky (vysoká, stredná, nízka alebo žiadna), ktorá udáva pravdepodobný rozsah dĺžky a typu zámky obrazovky. Aplikácia tiež navrhuje používateľom aktualizáciu zámky obrazovky na určitú úroveň, používatelia sa však môžu na základe vlastného uváženia rozhodnúť tento návrh ignorovať a prejsť inam. Upozorňujeme, že zámka obrazovky nie je uložená vo forme obyčajného textu, takže aplikácia nepozná presné heslo."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"používať biometrický hardvér"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Umožňuje aplikácii používať na overenie totožnosti biometrický hardvér"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"spravovať hardvér na snímanie odtlačkov prstov"</string>
@@ -1123,15 +1127,15 @@
<string name="deleteText" msgid="6979668428458199034">"Odstrániť"</string>
<string name="inputMethod" msgid="1653630062304567879">"Metóda vstupu"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Operácie s textom"</string>
- <string name="email" msgid="4560673117055050403">"E-mail"</string>
+ <string name="email" msgid="4560673117055050403">"Poslať e-mail"</string>
<string name="email_desc" msgid="3638665569546416795">"Napísať na vybratú e-mailovú adresu"</string>
<string name="dial" msgid="1253998302767701559">"Volať"</string>
<string name="dial_desc" msgid="6573723404985517250">"Zavolať na vybraté telefónne číslo"</string>
- <string name="map" msgid="5441053548030107189">"Mapa"</string>
+ <string name="map" msgid="5441053548030107189">"Otvoriť mapu"</string>
<string name="map_desc" msgid="1836995341943772348">"Nájsť vybranú adresu"</string>
<string name="browse" msgid="1245903488306147205">"Otvoriť"</string>
<string name="browse_desc" msgid="8220976549618935044">"Otvoriť vybratú webovú adresu"</string>
- <string name="sms" msgid="4560537514610063430">"Správa"</string>
+ <string name="sms" msgid="4560537514610063430">"Poslať správu"</string>
<string name="sms_desc" msgid="7526588350969638809">"Napísať SMS na vybraté telefónne číslo"</string>
<string name="add_contact" msgid="7867066569670597203">"Pridať"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Pridať medzi kontakty"</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet sa aktualizuje…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Zariadenie sa aktualizuje…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefón sa spúšťa…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Systém Android sa spúšťa…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet sa spúšťa…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Zariadenie sa spúšťa…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimalizuje sa úložisko"</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Klepnutím zobrazíte všetky siete"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Pripojiť"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Všetky siete"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Pripojené k sieti Wi‑Fi navrhnutej používateľom <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Chcete umožniť používateľovi <xliff:g id="NAME">%s</xliff:g> navrhovať siete za vás?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Áno"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nie"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi sa zapne automaticky"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Keď budete v blízkosti kvalitnej uloženej siete"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Znova nezapínať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0790e90..dcaa4f5 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Aplikaciji omogoča dostop do funkcij telefona v napravi. S tem dovoljenjem lahko aplikacija določi telefonsko številko in ID-je naprave, določi lahko tudi, ali je klic aktiven, in oddaljeno številko, s katero je klic povezan."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"Usmeri klice prek sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Dovoli, da aplikacija usmeri klice prek sistema, da se tako izboljša izkušnja klicanja."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ogled in nadzor klicev prek sistema."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Aplikaciji dovoljuje, da si ogleda in nadzoruje aktivne klice v napravi. To vključuje informacije, kot so telefonske številke klicev in stanje klicev."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"Nadaljevanje klica iz druge aplikacije"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Aplikaciji dovoljuje nadaljevanje klica, ki se je začel v drugi aplikaciji."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"branje telefonskih številk"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Podpira komunikacijo med računalnikom in oznakami, karticami in bralniki komunikacije s tehnologijo bližnjega polja."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"onemogočanje zaklepanja zaslona"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Aplikaciji dovoljuje, da onemogoči zaklep tipkovnice in morebitno povezano varnostno geslo. Telefon na primer onemogoči zaklep tipkovnice pri dohodnem klicu ter vnovič omogoči zaklep, ko je klic končan."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"pridobi in zahtevaj zapletenost zaklepanja zaslona"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Aplikaciji dovoljuje, da pridobi raven zapletenosti zaklepanja zaslona (visoka, srednja, nizka ali brez), ki označuje možen obseg dolžine in vrsto zaklepanja zaslona. Aplikacija lahko tudi predlaga uporabnikom, da posodobijo zaklepanje zaslona na določeno raven, vendar lahko uporabniki to opozorilo prezrejo in ga zaprejo tako, da se pomaknejo stran. Upoštevajte, da zaklepanje zaslona ni shranjeno v navadnem besedilu, tako da aplikacija ne pozna točnega gesla."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"uporaba strojne opreme za biometrične podatke"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Aplikaciji omogoča uporabo strojne opreme za biometrične podatke za preverjanje pristnosti"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje strojne opreme za prstne odtise"</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablični računalnik se posodablja …"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Naprava se posodablja …"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon se zaganja …"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android se zaganja …"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablični računalnik se zaganja …"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Naprava se zaganja …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje shrambe."</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Dotaknite se, če si želite ogledati vsa omrežja"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Vzpostavi povezavo"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Vsa omrežja"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Vzpostavljena povezava z omrežjem Wi‑Fi na predlog aplikacije <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Ali želite aplikaciji <xliff:g id="NAME">%s</xliff:g> dovoliti predlaganje omrežij?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Da"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ne"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Povezava Wi‑Fi se bo samodejno vklopila"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Ko ste v bližini zanesljivega shranjenega omrežja"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne vklopi znova"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 5c001ed..413ff8a 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Lejon aplikacionin të hyjë në funksionet telefonike të pajisjes. Kjo leje i mundëson aplikacionit të përcaktojë numrin e telefonit dhe ID-të e pajisjes, nëse një telefonatë është aktive apo nëse numri në distancë është i lidhur me një telefonatë."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"kalon telefonatat përmes sistemit"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Lejon që aplikacioni të kalojë telefonatat përmes sistemit për të përmirësuar përvojën e telefonatës."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Shiko dhe kontrollo telefonatat nëpërmjet sistemit."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Lejon që aplikacioni të shikojë dhe të kontrollojë telefonatat në vazhdim në pajisje. Kjo përfshin informacione si p.sh. numrat e telefonatave për telefonatat dhe gjendjen e telefonatave."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"vazhdo një telefonatë nga një aplikacion tjetër"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Lejon që aplikacioni të vazhdojë një telefonatë që është nisur në një aplikacion tjetër."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lexo numrat e telefonit"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Lejon aplikacionin të komunikojë me etiketimet e \"Komunikimit të fushës së afërt (NFC)\", kartat dhe lexuesit."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"çaktivizo kyçjen e ekranit"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Lejon aplikacionin të çaktivizojë kyçjen e tasteve dhe çdo mbrojtje të lidhur me fjalëkalimin. Për shembull, telefoni çaktivizon kyçjen e tasteve kur merr një telefonatë hyrëse, pastaj riaktivizon kyçjen e tasteve kur mbaron telefonata."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"merr dhe kërko kompleksitetin e kyçjes së ekranit"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Lejo që aplikacioni të mësojë nivelin e kompleksitetit të kyçjes së ekranit (i lartë, i mesëm, i ulët ose asnjë), që tregon gamën e mundshme të gjatësisë dhe llojin e kyçjes së ekranit. Aplikacioni mund t\'u sugjerojë gjithashtu përdoruesve që të përditësojnë kyçjen e ekranit në një nivel të caktuar, por përdoruesit mund ta shpërfillin lirshëm dhe të navigojnë më tej. Ki parasysh se kyçja e ekranit nuk ruhet në tekst të thjeshtë, prandaj aplikacioni nuk e di fjalëkalimin e saktë."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"përdor harduerin biometrik"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"E lejon aplikacionin që të përdorë harduerin biometrik për vërtetimin"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"të menaxhojë harduerin e gjurmës së gishtit"</string>
@@ -1087,19 +1091,19 @@
<string name="email_desc" msgid="3638665569546416795">"Dërgo email tek adresa e zgjedhur"</string>
<string name="dial" msgid="1253998302767701559">"Telefono"</string>
<string name="dial_desc" msgid="6573723404985517250">"Telefono në numrin e zgjedhur të telefonit"</string>
- <string name="map" msgid="5441053548030107189">"Harta"</string>
+ <string name="map" msgid="5441053548030107189">"Hap hartën"</string>
<string name="map_desc" msgid="1836995341943772348">"Gjej adresën e zgjedhur"</string>
<string name="browse" msgid="1245903488306147205">"Hap"</string>
<string name="browse_desc" msgid="8220976549618935044">"Hap URL-në e zgjedhur"</string>
- <string name="sms" msgid="4560537514610063430">"Mesazh"</string>
+ <string name="sms" msgid="4560537514610063430">"Dërgo mesazh"</string>
<string name="sms_desc" msgid="7526588350969638809">"Dërgo mesazh te numri i zgjedhur i telefonit"</string>
<string name="add_contact" msgid="7867066569670597203">"Shto"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Shto te kontaktet"</string>
<string name="view_calendar" msgid="979609872939597838">"Shiko"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Shiko kohën e zgjedhur në kalendar"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Orari"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Planifiko"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Planifiko ngjarjen për kohën e zgjedhur"</string>
- <string name="view_flight" msgid="7691640491425680214">"Pjesë muzikore"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Monitoro"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Monitoro fluturimin e zgjedhur"</string>
<string name="translate" msgid="9218619809342576858">"Përkthe"</string>
<string name="translate_desc" msgid="4502367770068777202">"Përkthe tekstin e zgjedhur"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tableti po përditësohet…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Pajisja po përditësohet…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefoni po niset…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"\"Androidi\" po fillon…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tableti po niset…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Pajisja po niset…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Po përshtat ruajtjen."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Trokit për të parë të gjitha rrjetet"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Lidhu"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Të gjitha rrjetet"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Lidhur me rrjetin Wi‑Fi të propozuar nga <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Dëshiron të lejosh që <xliff:g id="NAME">%s</xliff:g> të propozojë rrjetet për ty?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Po"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Jo"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi do të aktivizohet automatikisht"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kur ndodhesh pranë një rrjeti të ruajtur me cilësi të lartë"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Mos e aktivizo përsëri"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index b64f6fe..8f8fe0e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -450,6 +450,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дозвољава апликацији да приступа функцијама телефона на уређају. Ова дозвола омогућава апликацији да утврди број телефона и ИД-ове уређаја, затим да ли је позив активан, као и број даљинског уређаја са којим је успостављен позив."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"преусмеравање позива преко система"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозвољава апликацији да преусмерава позиве преко система да би побољшала доживљај позивања."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"преглед и контрола позива преко система."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Дозвољава апликацији да прегледа и контролише тренутне позиве на уређају. То обухвата информације попут бројева телефона и статуса позива."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"настави позив у другој апликацији"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Дозвољава апликацији да настави позив који је започет у другој апликацији."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"читање бројева телефона"</string>
@@ -510,6 +512,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Дозвољава апликацији да комуницира са ознакама, картицама и читачима комуникације кратког домета (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"онемогућавање закључавања екрана"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Дозвољава апликацији да онемогући закључавање тастатуре и све повезане безбедносне мере са лозинкама. На пример, телефон онемогућава закључавање тастатуре при пријему долазног телефонског позива, а затим га поново омогућава по завршетку позива."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"добијање и тражење нивоа сложености закључавања екрана"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Дозвољава апликацији да сазна ниво сложености закључавања екрана (висока, средња, ниска или ниједна), што указује на могући опсег трајања и тип закључавања екрана. Апликација може и да предлаже корисницима да ажурирају закључавање екрана на одређени ниво, али корисници слободно могу да занемаре то и да иду на друге странице. Имајте на уму да се подаци за закључавање екрана не чувају као обичан текст, па апликација не зна тачну лозинку."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"користи биометријски хардвер"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Дозвољава апликацији да користи биометријски хардвер за потврду идентитета"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"управљај хардвером за отиске прстију"</string>
@@ -1107,7 +1111,7 @@
<string name="email_desc" msgid="3638665569546416795">"Пошаљите имејл на изабрану адресу"</string>
<string name="dial" msgid="1253998302767701559">"Позови"</string>
<string name="dial_desc" msgid="6573723404985517250">"Позовите изабрани број телефона"</string>
- <string name="map" msgid="5441053548030107189">"Мапа"</string>
+ <string name="map" msgid="5441053548030107189">"Прикажи на мапи"</string>
<string name="map_desc" msgid="1836995341943772348">"Пронађите изабрану адресу"</string>
<string name="browse" msgid="1245903488306147205">"Отвори"</string>
<string name="browse_desc" msgid="8220976549618935044">"Отворите изабрани URL"</string>
@@ -1201,6 +1205,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Таблет се ажурира…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Уређај се ажурира…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон се покреће…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android се покреће…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Таблет се покреће…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Уређај се покреће…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Меморија се оптимизује."</string>
@@ -1261,6 +1266,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Додирните да бисте видели све мреже"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Повежи"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Све мреже"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Повезани сте са Wi‑Fi мрежом коју предлаже <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Желите ли да дозволите да вам <xliff:g id="NAME">%s</xliff:g> предлаже мреже?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Да"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Не"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ће се аутоматски укључити"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Када сте у близини сачуване мреже високог квалитета"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не укључуј поново"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 18c66ed..cd5580d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"dirigera samtal via systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Appen tillåts att dirigera samtal via systemet för att förbättra samtalsupplevelsen."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"visa och styra samtal via systemet."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Tillåter att appen kan visa och styra pågående samtal på enheten. Detta omfattar information som telefonnummer för samtal och samtalens status."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"fortsätta ett samtal från en annan app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Tillåter att appen fortsätter ett samtal som har startats i en annan app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"läsa telefonnummer"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Tillåter att appen kommunicerar med etiketter, kort och läsare för närfältskommunikation (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"inaktivera skärmlåset"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Tillåter att appen inaktiverar tangentlåset och tillhörande lösenordsskydd. Ett exempel kan vara att tangentlåset inaktiveras vid inkommande samtal och aktiveras igen när samtalet är avslutat."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"hämta och begär komplexitet för låsskärmen"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Tillåter att appen får reda på komplexitetsnivån för skärmlåset (hög, mellan, låg eller ingen). Detta ger en indikation på skärmlåsets möjliga längd och typ. Appen kan även föreslå att skärmlåset uppdateras till en viss nivå, men användare kan ignorera förslaget och fortsätta navigera. Observera att skärmlåset inte lagras i vanlig text så appen får inte reda på det exakta lösenordet."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"använd biometrisk maskinvara"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Tillåter att appen använder biometrisk maskinvara vid autentisering"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"hantera maskinvara för fingeravtryck"</string>
@@ -1087,7 +1091,7 @@
<string name="email_desc" msgid="3638665569546416795">"Skicka e-post till vald adress"</string>
<string name="dial" msgid="1253998302767701559">"Ring"</string>
<string name="dial_desc" msgid="6573723404985517250">"Ring valt telefonnummer"</string>
- <string name="map" msgid="5441053548030107189">"Karta"</string>
+ <string name="map" msgid="5441053548030107189">"Öppna karta"</string>
<string name="map_desc" msgid="1836995341943772348">"Hitta den valda adressen"</string>
<string name="browse" msgid="1245903488306147205">"Öppna"</string>
<string name="browse_desc" msgid="8220976549618935044">"Öppna vald webbadress"</string>
@@ -1097,9 +1101,9 @@
<string name="add_contact_desc" msgid="4830217847004590345">"Lägg till i Kontakter"</string>
<string name="view_calendar" msgid="979609872939597838">"Visa"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Visa vald tid i kalendern"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Schema"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Schemalägg"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Schemalägg händelse på den valda tiden"</string>
- <string name="view_flight" msgid="7691640491425680214">"Spår"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Spåra"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Spåra valt flyg"</string>
<string name="translate" msgid="9218619809342576858">"Översätt"</string>
<string name="translate_desc" msgid="4502367770068777202">"Översätt markerad text"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Surfplattan uppdateras …"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Enheten uppdateras …"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Mobilen startar …"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android startar …"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Surfplattan startar …"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Enheten startar …"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lagringsutrymmet optimeras."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tryck för att visa alla nätverk"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Anslut"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Alla nätverk"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Ansluten till Wi-Fi-nätverket som föreslogs i <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Vill du tillåta att <xliff:g id="NAME">%s</xliff:g> föreslår nätverk till dig?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ja"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Nej"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi aktiveras automatiskt"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"När du är i närheten av ett sparat nätverk av hög kvalitet"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Återaktivera inte"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 51ac513..bc3e4ad 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Huruhusu programu kufikia vipengele vya simu vilivyo kwenye kifaa. Idhini hii inaruhusu programu kutambua nambari ya simu na kifaa, kama kuna simu inayopigwa, na nambari ya mbali iliyounganishwa kwenye simu."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"elekeza simu kupitia mfumo"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Huruhusu programu kuelekeza simu zake kupitia mfumo ili kuboresha hali ya kupiga simu."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"kuona na kudhibiti simu kupitia mfumo."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Inaruhusu programu ione na kudhibiti simu zinazoendelea kupigwa kwenye kifaa. Hii inajumuisha maelezo kama vile nambari za simu zinazopigwa na hali ya simu."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"endelea na simu kutoka programu nyingine"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Huruhusu programu kuendelea na simu ambayo ilianzishwa katika programu nyingine."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"kusoma nambari za simu"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Inaruhusu programu kuwasiliana na lebo, kadi na wasomaji wa Near Field Communication (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"zima kufuli la skrini yako"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Inaruhusu programu kulemaza ufunguo wa vitufe na usalama mwingine ambata wa nenosiri. Kwa mfano, simu inalemaza ufunguo wa viitufe inapopokea simu inayoingia, kisha inawezesha upya ufunguo wa vitufe wakati simu inapokamilika."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"kupata na kuomba kiwango cha uchangamano wa kufunga skrini"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Huruhusu programu kupata maelezo kuhusu kiwango cha uchangamano wa kufunga skrini (juu, wastani, chini au hakuna), ambacho huashiria urefu unaowezekana na aina ya kufunga skrini. Programu pia inaweza kupendekeza kuwa watumiaji wasasishe kipengele cha kufunga skrini kiwe cha kiwango fulani lakini watumiaji wanaweza kupuuza kwa hiari na waende kwingine. Kumbuka kuwa maelezo ya kufunga skrini hayahifadhiwi kama maandishi, hivyo programu haitambui nenosiri mahususi."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"tumia maunzi ya kibiolojia"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Huruhusu programu itumie maunzi ya kibiolojia katika uthibitishaji"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"dhibiti maunzi ya kitambulisho"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Kompyuta kibao inasasishwa…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Kifaa kinasasishwa…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Simu inawaka…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Inaanzisha Android..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Kompyuta kibao inawaka…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Kifaa kiwaka…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Inaboresha hifadhi."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gusa ili uone mitandao yote"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Unganisha"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Mitandao yote"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Imeunganishwa na mtandao wa Wi‑Fi uliopendekezwa na <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Je, ungependa kuruhusu <xliff:g id="NAME">%s</xliff:g> akupendekezee mitandao?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ndiyo"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Hapana"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi itawashwa kiotomatiki"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Unapokuwa karibu na mtandao uliohifadhiwa wenye ubora wa juu"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Usiwashe tena"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 1f2c6a6..22da7b0 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"சாதனத்தின் மொபைல் அம்சங்களை அணுகப் பயன்பாட்டை அனுமதிக்கிறது. மொபைல் மற்றும் சாதன ஐடிகள், அழைப்பு செயலில் உள்ளதா மற்றும் அழைப்பு மூலம் இணைக்கப்பட்ட தொலைக் கட்டுப்பாட்டு எண் ஆகியவற்றைத் தீர்மானிக்க இந்த அனுமதி பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"சிஸ்டம் மூலம் அழைப்புகளை ரூட் செய்தல்"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"அழைக்கும் அனுபவத்தை மேம்படுத்தும் பொருட்டு, சிஸ்டம் மூலம் தனது அழைப்புகளை ரூட் செய்ய பயன்பாட்டை அனுமதிக்கும்."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"சிஸ்டம் மூலமாக அழைப்புகளைப் பார்த்தலும் கட்டுப்படுத்துதலும்."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"செயலில் உள்ள அழைப்புகளைச் சாதனத்தில் பார்க்கவும் கட்டுப்படுத்தவும் ஆப்ஸை அனுமதிக்கிறது. அழைப்பு எண்கள் மற்றும் அழைப்புகளின் நிலை போன்ற தகவல்களும் இதில் அடங்கும்."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"மற்றொரு பயன்பாட்டிலிருந்து அழைப்பைத் தொடருதல்"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"மற்றொரு பயன்பாட்டில் தொடங்கப்பட்ட அழைப்பைத் தொடருவதற்கு, பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ஃபோன் எண்களைப் படித்தல்"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"குறுகிய இடைவெளி தகவல்பரிமாற்றம் (NFC), குறிகள், கார்டுகள் மற்றும் ரீடர்கள் ஆகியவற்றுடன் தொடர்புகொள்ள, பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"உங்கள் திரைப் பூட்டை முடக்குதல்"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"விசைப்பூட்டையும், தொடர்புடைய கடவுச்சொல் பாதுகாப்பையும் முடக்கப் பயன்பாட்டை அனுமதிக்கிறது. எடுத்துக்காட்டாக, உள்வரும் மொபைல் அழைப்பைப் பெறும்போது மொபைல் விசைப்பூட்டை முடக்குகிறது, பிறகு அழைப்பு முடிந்தவுடன் விசைப்பூட்டை மீண்டும் இயக்குகிறது."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"திரைப் பூட்டு தொடர்பான சிக்கலைத் தீர்க்க அனுமதி பெறுதல்"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"திரைப் பூட்டு தொடர்பான சிக்கலின் தன்மையைப் பற்றி (அதிகம், நடுத்தரம், குறைவு அல்லது ஏதுமில்லை) அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கிறது. இதன் மூலம் திரைப் பூட்டின் திறனையும் வகையையும் பற்றி அறிந்துகொள்ள முடிகிறது. மேலும் திரைப் பூட்டு தொடர்பான சிக்கலின் தன்மையை குறிப்பிட்ட நிலைக்குப் புதுப்பித்துக்கொள்ளலாம் என்பதையும் ஆப்ஸ் பயனர்களுக்குப் பரிந்துரைக்கலாம். ஆனால் தங்கள் விருப்பப்படி அவற்றைப் பயனர்கள் ஏற்கவோ நிராகரிக்கவோ செய்யலாம். கவனத்திற்கு: திரைப் பூட்டு எளிய உரையிலான கடவுச்சொல்லால் சேமிக்கப்படுவதில்லை என்பதால் சரியான கடவுச்சொல்லை ஆப்ஸால் அறிய இயலாது."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"பயோமெட்ரிக் வன்பொருளைப் பயன்படுத்து"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"பயோமெட்ரிக் வன்பொருளைப் பயன்படுத்தி அங்கீகரிப்பதற்கு, பயன்பாட்டை அனுமதிக்கும்"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"கைரேகை வன்பொருளை நிர்வகி"</string>
@@ -1083,7 +1087,7 @@
<string name="deleteText" msgid="6979668428458199034">"நீக்கு"</string>
<string name="inputMethod" msgid="1653630062304567879">"உள்ளீட்டு முறை"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"உரை நடவடிக்கைகள்"</string>
- <string name="email" msgid="4560673117055050403">"மின்னஞ்சல்"</string>
+ <string name="email" msgid="4560673117055050403">"மின்னஞ்சல் அனுப்பு"</string>
<string name="email_desc" msgid="3638665569546416795">"தேர்ந்தெடுத்த முகவரிக்கு மின்னஞ்சல் அனுப்பும்"</string>
<string name="dial" msgid="1253998302767701559">"அழை"</string>
<string name="dial_desc" msgid="6573723404985517250">"தேர்ந்தெடுத்த ஃபோன் எண்ணை அழைக்கும்"</string>
@@ -1091,13 +1095,13 @@
<string name="map_desc" msgid="1836995341943772348">"தேர்ந்தெடுத்த முகவரிக்கான வரைபடத்தைத் திறக்கும்"</string>
<string name="browse" msgid="1245903488306147205">"திற"</string>
<string name="browse_desc" msgid="8220976549618935044">"தேர்ந்தெடுத்த URLஐத் திறக்கும்"</string>
- <string name="sms" msgid="4560537514610063430">"செய்தி"</string>
+ <string name="sms" msgid="4560537514610063430">"செய்தி அனுப்பு"</string>
<string name="sms_desc" msgid="7526588350969638809">"தேர்ந்தெடுத்த ஃபோன் எண்ணிற்குச் செய்தி அனுப்பும்"</string>
<string name="add_contact" msgid="7867066569670597203">"சேர்"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"தொடர்புகளில் சேர்க்கும்"</string>
<string name="view_calendar" msgid="979609872939597838">"காண்பி"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"கேலெண்டரில் தேர்ந்தெடுத்த நேரத்தைக் காட்டும்"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"திட்டமிடுதல்"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"திட்டமிடு"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"தேர்ந்தெடுத்த நேரத்திற்கு நிகழ்வைத் திட்டமிடும்"</string>
<string name="view_flight" msgid="7691640491425680214">"கண்கானி"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"தேர்ந்தெடுத்த விமானத்தைக் கண்காணிக்கும்"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"டேப்லெட் புதுப்பிக்கப்படுகிறது…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"சாதனம் புதுப்பிக்கப்படுகிறது…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"மொபைல் தொடங்குகிறது…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android துவங்குகிறது..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"டேப்லெட் தொடங்குகிறது…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"சாதனம் தொடங்குகிறது…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"சேமிப்பகத்தை உகந்ததாக்குகிறது."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"எல்லா நெட்வொர்க்குகளையும் பார்க்க, தட்டவும்"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"இணை"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"எல்லா நெட்வொர்க்குகளும்"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> இன் வைஃபை நெட்வொர்க்குடன் இணைக்கப்பட்டது"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"உங்களுக்கு நெட்வொர்க்குகளைக் காட்ட <xliff:g id="NAME">%s</xliff:g>ஐ அனுமதிக்கவா?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"சரி"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"வேண்டாம்"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"வைஃபை தானாக ஆன் ஆகும்"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"சேமித்த, உயர்தர நெட்வொர்க்கிற்கு அருகில் இருக்கும்போது"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"மீண்டும் ஆன் செய்யாதே"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index fd853fb..1cc0274 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"పరికరం యొక్క ఫోన్ ఫీచర్లను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి ఫోన్ నంబర్ మరియు పరికరం IDలను, కాల్ సక్రియంగా ఉందా లేదా అనే విషయాన్ని మరియు కాల్ ద్వారా కనెక్ట్ చేయబడిన రిమోట్ నంబర్ను కనుగొనడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"కాల్లను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్లను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"సిస్టమ్ ద్వారా కాల్లను చూసి, నియంత్రించండి."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"పరికరంలో కొనసాగుతున్న కాల్లను చూడడానికి మరియు నియంత్రించడానికి యాప్ను అనుమతిస్తుంది. ఇందులో కాల్ కోసం కాల్ల నంబర్లు మరియు రాష్ట్ర కాల్ వంటి సమాచారం ఉంటుంది."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"మరో యాప్ నుండి కాల్ని కొనసాగించండి"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"మరో యాప్లో ప్రారంభించిన కాల్ని కొనసాగించడానికి యాప్ని అనుమతిస్తుంది."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ఫోన్ నంబర్లను చదువు"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"సమీప ఫీల్డ్ కమ్యూనికేషన్ (NFC) ట్యాగ్లు, కార్డులు మరియు రీడర్లతో కమ్యూనికేట్ చేయడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"మీ స్క్రీన్ లాక్ను నిలిపివేయడం"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"కీలాక్ మరియు ఏదైనా అనుబంధించబడిన పాస్వర్డ్ భద్రతను నిలిపివేయడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్ను నిలిపివేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్ను మళ్లీ ప్రారంభిస్తుంది."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"స్క్రీన్ లాక్ పాస్వర్డ్ బల స్థాయిని పొందడం మరియు అభ్యర్ధించడం"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"ఇది మీ స్క్రీన్ లాక్ పాస్వర్డ్ బల స్థాయి (తీవ్రంగా ఉండాలా లేదా ఓ మోస్తరుగా ఉండాలా లేదా తక్కువ తీవ్రంగా గానీ అసలు ఏదీ కాకుండా ఉండాలా) అనే విషయాన్ని గుర్తుంచుకోవడానికి యాప్కి అనుమతిస్తుంది, అలాగే పాస్వర్డ్ పొడుగు ఎంత ఉండాలీ, ఏ రకమైన స్క్రీన్ లాక్ పధ్ధతి అనుసరించాలో సూచిస్తుంది. యాప్ ఇంకా స్క్రీన్ లాక్ పాస్వర్డ్ బల స్థాయిని ఏ స్థాయిలో సెట్ చేసుకుంటే బాగుంటుందో వినియోగదారులకు తెలపగలదు కానీ వినియోగదారులు దాని సూచనలను పట్టించుకోకుండా వారి ఇష్టం మేరకు చక్కగా సెట్ చేసుకోవచ్చు. మీకో ముఖ్యమైన మాట స్క్రీన్ లాక్ పాస్వర్డ్ అనేది సాదా వచన రూపంలో నిల్వ చేయబడదు, కనుక అసలు మీరు పాస్వర్డ్ ఏం పెట్టారనే విషయం యాప్కి తెలియదు."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"బయోమెట్రిక్ హార్డ్వేర్ని ఉపయోగించు"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ప్రమాణీకరణ కోసం బయోమెట్రిక్ హార్డ్వేర్ను ఉపయోగించడానికి యాప్ని అనుమతిస్తుంది"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"వేలిముద్ర హార్డ్వేర్ని నిర్వహించడానికి అనుమతి"</string>
@@ -1083,27 +1087,27 @@
<string name="deleteText" msgid="6979668428458199034">"తొలగించు"</string>
<string name="inputMethod" msgid="1653630062304567879">"ఇన్పుట్ పద్ధతి"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"వచనానికి సంబంధించిన చర్యలు"</string>
- <string name="email" msgid="4560673117055050403">"ఇమెయిల్"</string>
+ <string name="email" msgid="4560673117055050403">"ఇమెయిల్ చేయి"</string>
<string name="email_desc" msgid="3638665569546416795">"ఎంచుకున్న చిరునామాకు ఇమెయిల్ను పంపుతుంది"</string>
- <string name="dial" msgid="1253998302767701559">"కాల్ చేయండి"</string>
+ <string name="dial" msgid="1253998302767701559">"కాల్ చేయి"</string>
<string name="dial_desc" msgid="6573723404985517250">"ఎంచుకున్న ఫోన్ నంబర్కు కాల్ చేస్తుంది"</string>
- <string name="map" msgid="5441053548030107189">"మ్యాప్"</string>
+ <string name="map" msgid="5441053548030107189">"మ్యాప్ తెరువు"</string>
<string name="map_desc" msgid="1836995341943772348">"ఎంచుకున్న చిరునామాను గుర్తించు"</string>
- <string name="browse" msgid="1245903488306147205">"తెరవండి"</string>
+ <string name="browse" msgid="1245903488306147205">"తెరువు"</string>
<string name="browse_desc" msgid="8220976549618935044">"ఎంచుకున్న URLని తెరుస్తుంది"</string>
- <string name="sms" msgid="4560537514610063430">"సందేశం"</string>
+ <string name="sms" msgid="4560537514610063430">"సందేశం పంపు"</string>
<string name="sms_desc" msgid="7526588350969638809">"ఎంచుకున్న ఫోన్ నంబర్కి సందేశం పంపుతుంది"</string>
- <string name="add_contact" msgid="7867066569670597203">"జోడించండి"</string>
+ <string name="add_contact" msgid="7867066569670597203">"జోడించు"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"పరిచయాలకు జోడిస్తుంది"</string>
- <string name="view_calendar" msgid="979609872939597838">"చూడండి"</string>
+ <string name="view_calendar" msgid="979609872939597838">"చూడు"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ఎంచుకున్న సమయాన్ని క్యాలెండర్లో వీక్షించండి"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"షెడ్యూల్"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"షెడ్యూల్ చేయి"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ఎంచుకున్న సమయానికి ఈవెంట్ను షెడ్యూల్ చేస్తుంది"</string>
- <string name="view_flight" msgid="7691640491425680214">"ట్రాక్ చేయండి"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ట్రాక్ చేయి"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ఎంచుకున్న విమానాన్ని ట్రాక్ చేస్తుంది"</string>
- <string name="translate" msgid="9218619809342576858">"అనువదించండి"</string>
+ <string name="translate" msgid="9218619809342576858">"అనువదించు"</string>
<string name="translate_desc" msgid="4502367770068777202">"ఎంచుకున్న వచనాన్ని అనువదించండి"</string>
- <string name="define" msgid="7394820043869954211">"నిర్వచించండి"</string>
+ <string name="define" msgid="7394820043869954211">"నిర్వహించు"</string>
<string name="define_desc" msgid="7910883642444919726">"ఎంచుకున్న వచనాన్ని నిర్వచించండి"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"నిల్వ ఖాళీ అయిపోతోంది"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"టాబ్లెట్ అప్డేట్ అవుతోంది…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"పరికరం అప్డేట్ అవుతోంది…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"ఫోన్ ప్రారంభమవుతోంది…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ప్రారంభమవుతోంది…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"టాబ్లెట్ ప్రారంభమవుతోంది…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"పరికరం ప్రారంభమవుతోంది…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"నిల్వను అనుకూలపరుస్తోంది."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"అన్ని నెట్వర్క్లు చూడటానికి నొక్కండి"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"కనెక్ట్ చేయి"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"అన్ని నెట్వర్క్లు"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> సూచన మేరకు Wi‑Fi నెట్వర్క్కు కనెక్ట్ చేయబడింది"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"మీరు <xliff:g id="NAME">%s</xliff:g> మీకోసం సూచించే నెట్వర్క్లకు అనుమతిస్తారా?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"అవును"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"లేదు"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi స్వయంచాలకంగా ఆన్ అవుతుంది"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"మీరు అధిక నాణ్యత గల సేవ్ చేసిన నెట్వర్క్కు సమీపంగా ఉన్నప్పుడు"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"తిరిగి ఆన్ చేయవద్దు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 9ce2930..2edf57c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"อนุญาตให้แอปพลิเคชันเข้าถึงฟีเจอร์โทรศัพท์ของอุปกรณ์ การอนุญาตนี้ทำให้แอปพลิเคชันสามารถตรวจสอบหมายเลขโทรศัพท์และรหัสอุปกรณ์ ตรวจสอบว่ามีการโทรที่ทำงานอยู่หรือไม่ และตรวจสอบหมายเลขระยะไกลที่เชื่อมต่อด้วยการโทร"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"กำหนดเส้นทางการโทรผ่านระบบ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"อนุญาตให้แอปกำหนดเส้นทางการโทรของแอปผ่านระบบเพื่อปรับปรุงประสบการณ์ในการโทร"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"ดูและจัดการการติดต่อผ่านระบบ"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"อนุญาตให้แอปดูและจัดการการติดต่อในอุปกรณ์ ซึ่งรวมถึงข้อมูลอย่างเช่น หมายเลขที่ใช้ในการติดต่อและสถานะการติดต่อ"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ต่อสายจากแอปอื่น"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"อนุญาตให้แอปต่อสายที่เริ่มจากแอปอื่น"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"อ่านหมายเลขโทรศัพท์"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"อนุญาตให้แอปพลิเคชันสื่อสารกับแท็ก Near Field Communication (NFC) การ์ด และโปรแกรมอ่าน"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ปิดใช้งานการล็อกหน้าจอของคุณ"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"อนุญาตให้แอปพลิเคชันปิดใช้งานการล็อกปุ่มกดและการรักษาความปลอดภัยด้วยรหัสผ่านใดๆ ที่เกี่ยวข้อง ตัวอย่างเช่น โทรศัพท์ปิดใช้งานการล็อกปุ่มกดเมื่อรับสายเรียกเข้า จากนั้นจึงเปิดใช้งานการล็อกปุ่มกดใหม่หลังจากวางสาย"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ใช้และขอดูความซับซ้อนของ \"ล็อกหน้าจอ\""</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"อนุญาตให้แอปเรียนรู้ระดับความซับซ้อนของการล็อกหน้าจอ (สูง ปานกลาง ต่ำ หรือไม่มี) ซึ่งแสดงให้เห็นช่วงความยาวและประเภทของการล็อกหน้าจอที่เป็นไปได้ นอกจากนี้แอปยังแนะนำให้ผู้ใช้อัปเดตการล็อกหน้าจอเป็นระดับหนึ่งๆ ได้ด้วย แต่ผู้ใช้จะปฏิเสธและไปยังส่วนต่างๆ ต่อได้ โปรดทราบว่าระบบไม่ได้จัดเก็บการล็อกหน้าจอไว้เป็นข้อความธรรมดา เพื่อให้แอปไม่รู้รหัสผ่าน"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ใช้ฮาร์ดแวร์ชีวมิติ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"อนุญาตให้แอปใช้ฮาร์ดแวร์ชีวมิติเพื่อตรวจสอบสิทธิ์"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"จัดการฮาร์ดแวร์ลายนิ้วมือ"</string>
@@ -1097,14 +1101,14 @@
<string name="add_contact_desc" msgid="4830217847004590345">"เพิ่มในรายชื่อติดต่อ"</string>
<string name="view_calendar" msgid="979609872939597838">"ดู"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"ดูเวลาที่เลือกในปฏิทิน"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"กำหนดการ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ตั้งเวลา"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"ตั้งเวลากิจกรรมสำหรับเวลาที่เลือก"</string>
- <string name="view_flight" msgid="7691640491425680214">"แทร็ก"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ติดตาม"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"ติดตามเที่ยวบินที่เลือก"</string>
<string name="translate" msgid="9218619809342576858">"แปล"</string>
<string name="translate_desc" msgid="4502367770068777202">"แปลข้อความที่เลือก"</string>
<string name="define" msgid="7394820043869954211">"หาความหมาย"</string>
- <string name="define_desc" msgid="7910883642444919726">"ให้คำจำกัดความข้อความที่เลือก"</string>
+ <string name="define_desc" msgid="7910883642444919726">"หาความหมายข้อความที่เลือก"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"แท็บเล็ตกำลังอัปเดต…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"อุปกรณ์กำลังอัปเดต…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"โทรศัพท์กำลังเริ่มต้น…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android กำลังเริ่มต้น…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"แท็บเล็ตกำลังเริ่มต้น…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"อุปกรณ์กำลังเริ่มต้น…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"กำลังเพิ่มประสิทธิภาพพื้นที่จัดเก็บข้อมูล"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"แตะเพื่อดูเครือข่ายทั้งหมด"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"เชื่อมต่อ"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"เครือข่ายทั้งหมด"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"เชื่อมต่ออยู่กับเครือข่าย Wi‑Fi ที่ <xliff:g id="NAME">%s</xliff:g> แนะนำ"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"ต้องการให้ <xliff:g id="NAME">%s</xliff:g> แนะนำเครือข่ายให้ไหม"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ใช่"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ไม่"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi จะเปิดโดยอัตโนมัติ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"เมื่อคุณอยู่ใกล้เครือข่ายคุณภาพสูงที่บันทึกไว้"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ไม่ต้องเปิดอีกครั้ง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 1193625..f7995f7 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Pinapayagan ang app na i-access ang mga tampok ng telepono ng device. Pinapayagan ng pahintulot na ito ang app na tukuyin ang numero ng telepono at mga ID ng device, kung aktibo man ang isang tawag, at ang malayuang numerong ikinonekta ng isang tawag."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"iruta ang mga tawag sa pamamagitan ng system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Pinapayagan ang app na iruta ang mga tawag nito sa pamamagitan ng system upang mapahusay ang karanasan sa pagtawag."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"tingnan at kontrolin ang mga tawag sa pamamagitan ng system."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Binibigyang-daan ang app na makita at makontrol ang mga kasalukuyang tawag sa device. Kabilang dito ang impormasyon gaya ng mga numero ng tawag para sa mga tawag at ang status ng mga tawag."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"ipagpatuloy ang isang tawag mula sa ibang app"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Pinapayagan ang app na ipagpatuloy ang isang tawag na sinimulan sa ibang app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"basahin ang mga numero ng telepono"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Pinapayagan ang app na makipag-ugnay sa Near Field Communication (NFC) na mga tag, card, at reader."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"i-disable ang iyong screen lock"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Pinapayagan ang app na i-disable ang keylock at anumang nauugnay na seguridad sa password. Halimbawa, hindi pinapagana ng telepono ang keylock kapag nakakatanggap ng papasok na tawag sa telepono, pagkatapos ay muling pinapagana ang keylock kapag tapos na ang tawag."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"kunin at humiling ng pagiging kumplikado ng lock ng screen"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Pinapayagan ang app na malaman ang antas ng pagiging kumplikado ng lock ng screen (mataas, katamtaman, mababa, o wala), na nagsasaad ng posibleng hanay ng haba at uri ng lock ng screen. Maaari ding magmungkahi ang app sa mga user na i-update nila ang lock ng screen sa isang partikular na antas ngunit malaya ang mga user na balewalain ito at mag-navigate palayo. Tandaang hindi naka-store bilang plaintext ang lock ng screen kaya hindi alam ng app ang eksaktong password."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"gumamit ng biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Pinapayagan ang app na gumamit ng biometric hardware para sa pag-authenticate"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"pamahalaan ang hardware ng fingerprint"</string>
@@ -1085,13 +1089,13 @@
<string name="editTextMenuTitle" msgid="4909135564941815494">"Pagkilos ng teksto"</string>
<string name="email" msgid="4560673117055050403">"Mag-email"</string>
<string name="email_desc" msgid="3638665569546416795">"Mag-email sa mga piniling address"</string>
- <string name="dial" msgid="1253998302767701559">"Tawagan"</string>
+ <string name="dial" msgid="1253998302767701559">"Tumawag"</string>
<string name="dial_desc" msgid="6573723404985517250">"Tawagan ang piniling numero ng telepono"</string>
- <string name="map" msgid="5441053548030107189">"Mapa"</string>
+ <string name="map" msgid="5441053548030107189">"Mag-mapa"</string>
<string name="map_desc" msgid="1836995341943772348">"Hanapin ang piniling address"</string>
<string name="browse" msgid="1245903488306147205">"Buksan"</string>
<string name="browse_desc" msgid="8220976549618935044">"Buksan ang piniling URL"</string>
- <string name="sms" msgid="4560537514610063430">"Padalhan ng Mensahe"</string>
+ <string name="sms" msgid="4560537514610063430">"Magmensahe"</string>
<string name="sms_desc" msgid="7526588350969638809">"Padalhan ng mensahe ang piniling numero ng telepono"</string>
<string name="add_contact" msgid="7867066569670597203">"Magdagdag"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Idagdag sa mga contact"</string>
@@ -1101,8 +1105,8 @@
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Mag-iskedyul ng event para sa piniling oras"</string>
<string name="view_flight" msgid="7691640491425680214">"Subaybayan"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"I-track ang piniling flight"</string>
- <string name="translate" msgid="9218619809342576858">"Isalin"</string>
- <string name="translate_desc" msgid="4502367770068777202">"Isalin ang napiling text"</string>
+ <string name="translate" msgid="9218619809342576858">"I-translate"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"I-translate ang piniling text"</string>
<string name="define" msgid="7394820043869954211">"Ilarawan"</string>
<string name="define_desc" msgid="7910883642444919726">"Ilarawan ang piniling text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Nag-a-update ang tablet…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Nag-a-update ang device…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Nagsisimula ang telepono…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Nagsisimula ang Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Nagsisimula ang tablet…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Nagsisimula ang device…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ino-optimize ang storage."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"I-tap upang makita ang lahat ng network"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Kumonekta"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Lahat ng network"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Nakakonekta sa Wi‑Fi network na iminungkahi ng <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Gusto mo bang hayaan ang <xliff:g id="NAME">%s</xliff:g> na magmungkahi ng mga network para sa iyo?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Oo"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Hindi"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Awtomatikong mag-o-on ang Wi‑Fi"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kapag malapit ka sa naka-save na network na mataas ang kalidad"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Huwag i-on muli"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 521da3f..761eba6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Uygulamaya cihazdaki telefon özelliklerine erişme izni verir. Bu izin, uygulamanın telefon numarasını ve cihaz kimliğini, etkin bir çağrı olup olmadığını ve çağrıda bağlanılan karşı tarafın numarasını öğrenmesine olanak sağlar."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"çağrıları sistem üzerinden yönlendir"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Uygulamanın, çağrı deneyimini iyileştirmek için çağrılarını sistem üzerinden yönlendirmesine olanak tanır."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"aramaları sistemde görüp denetleme."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Uygulamanın cihazda devam eden aramaları görmesini ve denetlemesini sağlar. Bu bilgiler arasında aramaların yapıldığı numaralar ve aramaların durumu gibi bilgiler yer alır."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"başka bir uygulamadaki çağrıya devam etme"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Uygulamanın, başka bir uygulamada başlatılan çağrıya devam etmesine izin verir."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon numaralarını oku"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Uygulamaya, Near Field Communication (NFC) etiketleri, kartlar ve okuyucular ile iletişim kurma izni verir."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ekran kilidimi devre dışı bırak"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Uygulamaya, tuş kilidini ve ilişkili tüm şifreli güvenlik önlemlerini devre dışı bırakma izni verir. Örneğin, telefon, çağrı alındığında tuş kilidinin devre dışı bırakır ve sonra, görüşme bittiğinde kilidi yeniden etkinleştirir."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"ekran kilidi karmaşıklığını edinme ve isteme"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Uygulamanın, ekran kilidi karmaşıklık seviyesini (yüksek, orta, düşük veya yok) öğrenmesini sağlar. Ekran kilidi karmaşıklık seviyesi, ekran kilidinin olası uzunluk aralığını ve türünü gösterir. Uygulama, kullanıcılara ekran kilidini belirli bir seviyeye güncellemelerini de önerebilir, ancak kullanıcılar bunu istedikleri gibi yoksayabilir ve geçebilirler. Ekran kilidi şifrelenmemiş metin olarak saklanmadığı için uygulamanın şifreyi tam olarak bilmediğini unutmayın."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"biyometrik donanım kullan"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Uygulamanın kimlik doğrulama için biyometrik donanım kullanmasına izin verir"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"parmak izi donanımını yönetme"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Tablet güncelleniyor…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Cihaz güncelleniyor…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon başlatılıyor…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android başlatılıyor…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Tablet başlatılıyor…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Cihaz başlatılıyor…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Depolama optimize ediliyor."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tüm ağları görmek için dokunun"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Bağlan"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tüm ağlar"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> tarafından önerilen kablosuz ağa bağlanıldı"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> uygulamasının sizin için ağ önerisinde bulunmasına izin vermek istiyor musunuz?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Evet"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Hayır"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Kablosuz özelliği otomatik olarak açılacak"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Daha önce kaydedilmiş yüksek kaliteli bir ağın yakınında olduğunuzda"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Tekrar açılmasın"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 2bac0c3..e7e99ae 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -453,6 +453,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дозволяє програмі отримувати доступ до телефонних функцій пристрою. Такий дозвіл дає програмі змогу визначати номер телефону й ідентифікатори пристрою, активність виклику, а також віддалений номер, на який здійснюється виклик."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"маршрутизувати виклики через систему"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозволяє додатку маршрутизувати виклики через систему, щоб було зручніше телефонувати."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"Переглядати виклики через систему й керувати ними."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Додаток може переглядати поточні виклики на пристрої та керувати ними. Це стосується такої інформації, як номери та стан викликів."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"продовжувати виклик з іншого додатка"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Додаток може продовжувати виклик, початий в іншому додатку."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"переглядати номери телефону"</string>
@@ -513,6 +515,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Дозволяє програмі обмінюватися даними з тегами, картками та читачами екрана Near Field Communication (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"вимикати блокування екрана"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Дозволяє програмі вимикати блокування клавіатури та будь-який пов’язаний паролем захист. Наприклад: телефон вимикає блокування клавіатури під час отримання вхідного дзвінка, після закінчення якого блокування клавіатури відновлюється."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"отримувати надсилати запити щодо рівня складності пароля для блокування екрана"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Дає змогу додатку визначати рівень складності пароля для блокування екрана (високий, середній, низький або нульовий), що вказує на можливий діапазон його довжини й тип. Додаток також може пропонувати користувачам підвищити рівень складності пароля для блокування екрана, але цю пропозицію можна ігнорувати. Примітка: оскільки пароль зашифрований, додаток його не знає."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"використовувати біометричне апаратне забезпечення"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Додаток може використовувати біометричне апаратне забезпечення для автентифікації"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"керувати апаратним забезпеченням для цифрових відбитків"</string>
@@ -1123,15 +1127,15 @@
<string name="deleteText" msgid="6979668428458199034">"Видалити"</string>
<string name="inputMethod" msgid="1653630062304567879">"Метод введення"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Дії з текстом"</string>
- <string name="email" msgid="4560673117055050403">"Електронна пошта"</string>
+ <string name="email" msgid="4560673117055050403">"Написати лист"</string>
<string name="email_desc" msgid="3638665569546416795">"Надіслати електронний лист на вибрану адресу"</string>
- <string name="dial" msgid="1253998302767701559">"Зателефонувати"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Набрати вибраний номер телефону"</string>
- <string name="map" msgid="5441053548030107189">"Карта"</string>
+ <string name="dial" msgid="1253998302767701559">"Телефонувати"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Телефонувати за вибраним номером"</string>
+ <string name="map" msgid="5441053548030107189">"Відкрити карту"</string>
<string name="map_desc" msgid="1836995341943772348">"Знайти вибрану адресу"</string>
<string name="browse" msgid="1245903488306147205">"Відкрити"</string>
<string name="browse_desc" msgid="8220976549618935044">"Відкрити вибрану URL-адресу"</string>
- <string name="sms" msgid="4560537514610063430">"Повідомлення"</string>
+ <string name="sms" msgid="4560537514610063430">"Написати SMS"</string>
<string name="sms_desc" msgid="7526588350969638809">"Надіслати повідомлення за вибраним номером телефону"</string>
<string name="add_contact" msgid="7867066569670597203">"Додати"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Додати в контакти"</string>
@@ -1221,6 +1225,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Планшет оновлюється…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Пристрій оновлюється…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Телефон запускається…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Запуск ОС Android…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Планшет запускається…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Пристрій запускається…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимізація пам’яті."</string>
@@ -1283,6 +1288,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Торкніться, щоб побачити всі мережі"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Під’єднатися"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Усі мережі"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Під’єднано до мережі Wi-Fi, яку запропонував додаток <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Дозволити додатку <xliff:g id="NAME">%s</xliff:g> пропонувати вам мережі?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Так"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Ні"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi вмикатиметься автоматично"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Коли ви поблизу збереженої мережі високої якості"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не вмикати знову"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2266221..e33f3f2 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ایپ کو آلے کی فون والی خصوصیات تک رسائی حاصل کرنے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو فون نمبر اور آلے کے IDs کا تعین کرنے، آیا کوئی کال فعال ہے، اور کال کے ذریعہ مربوط ریموٹ نمبر کا تعین کرنے دیتی ہے۔"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"سسٹم کے ذریعہ کالز روٹ کریں"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"کالںگ کا تجربہ بہتر بنانے کے لیے سسٹم کے ذریعہ ایپ کو کالز روٹ کرنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"سسٹم کے ذریعے کالز دیکھیں اور کنٹرول کریں۔"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"ایپ کو آلہ پر جاری کالز دیکھنے اور کنٹرول کرنے کی اجازت دیں۔ اس میں کالز کیلئے کال نمبرز اور کالز کی حالت جیسی معلومات شامل ہیں۔"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"دوسری ایپ کی کال جاری رکھیں"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ایپ کو دوسری ایپ میں شروع کردہ کال کو جاری رکھنے کی اجازت ملتی ہے۔"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"فون نمبرز پڑھیں"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"ایپ کو Near Field Communication (NFC) ٹیگز، کارڈز اور ریڈرز کے ساتھ مواصلت کرنے کی اجازت دیٹا ہے۔"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"اپنے اسکرین لاک کو غیر فعال کریں"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ایپ کو کلیدی لاک اور کسی بھی متعلقہ پاس ورڈ سیکیورٹی کو غیر فعال کرنے کی اجازت دیتا ہے۔ مثلاً، کوئی آنے والی فون کال موصول ہونے کے وقت فون کلیدی لاک کو غیر فعال کرتا ہے، پھر کال پوری ہوجانے پر کلیدی لاک کو دوبارہ فعال کردیتا ہے۔"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"موصول کریں اور اسکرین لاک کی پیچیدگی کے لیے درخواست کریں"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"اپپ کو اسکرین لاک کی پیچیدگی (مُشکل، درمیانی، آسان یا کوئی نہیں) کو جاننے کی اجازت دیتا ہے، جو لمبائی کی ممکنہ حد اور اسکرین لاک کے قسم کو بتاتا ہے۔ اپپ صارفین کو مشوره بھی دے سکتی ہے کہ وہ اسکرین لاک کو مخصوس لیول تک اپ ڈیٹ کرتے ہیں لیکن صارفین آزادانہ طور پر نظر انداز اور نیویگیٹ کر سکتے ہیں۔ یاد رکھیں کہ اسکرین لاک پلین ٹیکسٹ میں اسٹور کیا ہوا نہیں ہے اس لیے اپپ صحیح پاس ورڈ نہیں جانتی ہے۔"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"بایومیٹرک ہارڈ ویئر استعمال کریں"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ایپ کو توثیق کے لیے بایومیٹرک ہارڈ ویئر استعمال کرنے کی اجازت دیتا ہے"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"فنگر پرنٹ ہارڈ ویئر کا نظم کریں"</string>
@@ -1083,15 +1087,15 @@
<string name="deleteText" msgid="6979668428458199034">"حذف کریں"</string>
<string name="inputMethod" msgid="1653630062304567879">"اندراج کا طریقہ"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"متن کی کارروائیاں"</string>
- <string name="email" msgid="4560673117055050403">"ای میل"</string>
+ <string name="email" msgid="4560673117055050403">"ای میل بھیجیں"</string>
<string name="email_desc" msgid="3638665569546416795">"منتخب کردہ پتہ پر ای میل کریں"</string>
<string name="dial" msgid="1253998302767701559">"کال کریں"</string>
<string name="dial_desc" msgid="6573723404985517250">"منتخب کردہ فون نمبر پر کال کریں"</string>
- <string name="map" msgid="5441053548030107189">"نقشہ"</string>
+ <string name="map" msgid="5441053548030107189">"نقشہ کی ایپ کھولیں"</string>
<string name="map_desc" msgid="1836995341943772348">"منتخب کردہ پتہ تلاش کریں"</string>
<string name="browse" msgid="1245903488306147205">"کھولیں"</string>
<string name="browse_desc" msgid="8220976549618935044">"منتخب کردہ URL کھولیں"</string>
- <string name="sms" msgid="4560537514610063430">"پیغام"</string>
+ <string name="sms" msgid="4560537514610063430">"پیغام بھیجیں"</string>
<string name="sms_desc" msgid="7526588350969638809">"منتخب کردہ فون نمبر پر پیغام بھیجیں"</string>
<string name="add_contact" msgid="7867066569670597203">"شامل کریں"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"رابطوں میں شامل کریں"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"ٹیبلٹ اپ ڈیٹ ہو رہا ہے…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"آلہ اپ ڈیٹ ہو رہا ہے…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"فون شروع ہو رہا ہے…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android شروع ہو رہا ہے…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"ٹیبلیٹ شروع ہو رہا ہے…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"آلہ شروع ہو رہا ہے…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"اسٹوریج کو بہترین بنایا جا رہا ہے۔"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"تمام نیٹ ورکس دیکھنے کیلئے تھپتھپائيں"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"منسلک کریں"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"سبھی نیٹ ورکس"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> کے ذریعے تجویز کردہ Wi-Fi نیٹ ورک سے منسلک کر دیا گیا"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"کیا آپ <xliff:g id="NAME">%s</xliff:g> کو آپ کے لیے نیٹ ورکس تجویز کرنے کی اجازت دینا چاہتے ہیں؟"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ہاں"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"نہیں"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi از خود آن ہو جائے گا"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"جب آپ اعلی معیار کے محفوظ کردہ نیٹ ورک کے قریب ہوں"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"دوبارہ آن نہ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 204df4e..7742c3e 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ilovaga qurilmangizdagi telefon xususiyatlariga kirishga ruxsat beradi. Bu ruxsat ilovaga telefon raqami va qurilma nomlari, qo‘ng‘iroq faol yoki faolsizligi va masofadagi raqam qo‘ng‘rioq orqali bog‘langanligini aniqlashga imkon beradi."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"chaqiruvlarni tizim orqali yo‘naltirish"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ilova aloqa sifatini yaxshilash maqsadida chaqiruvlarni tizim orqali yo‘naltirishi mumkin."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"tizim orqali chaqiruvlarni tekshirish va boshqarish."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ilovaga qurilmadagi amaldagi chaqiruvlarni tekshirish va boshqarish imkonini beradi. Shuningdek, chaqiruvlar holati va kiruvchi-chiquvchi chaqiruv raqamlari axborotiga ham ruxsat beradi."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"chaqiruvni boshqa ilovada davom ettirish"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ilovaga boshqa ilovada boshlangan chaqiruvni davom ettirish imkon beradi"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon raqamlarini o‘qish"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Ilova qisqa masofali aloqa (NFC) texnologiyasi yordamida NFC yorliqlari, kartalar va o‘qish moslamalari bilan ma’lumot almashishi mumkin."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"ekran qulfini o‘chirib qo‘yish"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ilovaga ekran qulfini va har qanday parol yordamidagi xavfsizlik himoyalarini o‘chirishga ruxsat beradi. Masalan, kirish qo‘ng‘irog‘ida telefon ekran qulfini o‘chiradi va qo‘ng‘iroq tugashi bilan qulfni yoqadi."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"Ekran qulfining qiyinlik darajasini oshirish haqidagi axborotga ruxsat va tavsiyalar berish"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ilova ekran qulfining qiyinlik darajasi (yuqori, oʻrta, past yoki hech qanday), jumladan, qulf turi va parol uzunligi haqida axborotga ruxsat oladi. Bundan tashqari, foydalanuvchilarga qulflash qiyinligi darajasini oshirish taklif etiladi. Bu tavsiyalar majburiy emas. Parollar ochiq matn sifatida saqlanmaydi va ilova uni ocha olmaydi."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"biometrik sensordan foydalanish"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Haqiqiylikni tekshirish uchun biometrik sensordan foydalanish imkonini beradi"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"barmoq izi skanerini boshqarish"</string>
@@ -1084,22 +1088,22 @@
<string name="inputMethod" msgid="1653630062304567879">"Kiritish uslubi"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Matn yozish"</string>
<string name="email" msgid="4560673117055050403">"E-pochta"</string>
- <string name="email_desc" msgid="3638665569546416795">"Belgilangan manzilga xat yuborish"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Belgilangan e-pochta manziliga xat yuborish"</string>
<string name="dial" msgid="1253998302767701559">"Chaqiruv"</string>
<string name="dial_desc" msgid="6573723404985517250">"Belgilangan raqamga telefon qilish"</string>
<string name="map" msgid="5441053548030107189">"Xarita"</string>
<string name="map_desc" msgid="1836995341943772348">"Belgilangan manzilni xaritadan topish"</string>
<string name="browse" msgid="1245903488306147205">"Ochish"</string>
<string name="browse_desc" msgid="8220976549618935044">"Belgilangan URL manzilini ochish"</string>
- <string name="sms" msgid="4560537514610063430">"Xabar"</string>
+ <string name="sms" msgid="4560537514610063430">"SMS yozish"</string>
<string name="sms_desc" msgid="7526588350969638809">"Belgilangan telefon raqamiga SMS yuborish"</string>
- <string name="add_contact" msgid="7867066569670597203">"Qo‘shish"</string>
+ <string name="add_contact" msgid="7867066569670597203">"Saqlab olish"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Kontaktlarga saqlash"</string>
<string name="view_calendar" msgid="979609872939597838">"Ochish"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Taqvimda belgilangan vaqtni ko‘rish"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Jadval"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Belgilangan vaqt uchun tadbirni rejalashtirish"</string>
- <string name="view_flight" msgid="7691640491425680214">"Trek"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Belgilangan vaqtni taqvimda ochish"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Rejalashtirish"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Belgilangan vaqt uchun tadbir rejalashtirish"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Kuzatish"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Belgilangan parvozni kuzatish"</string>
<string name="translate" msgid="9218619809342576858">"Tarjima qilish"</string>
<string name="translate_desc" msgid="4502367770068777202">"Belgilangan matnni tarjima qilish"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Planshet yangilanmoqda…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Qurilma yangilanmoqda…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Telefon ishga tushirilmoqda…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android ishga tushmoqda…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Planshet ishga tushirilmoqda…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Qurilma ishga tushirilmoqda…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Xotira optimallashtirilmoqda."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Barcha tarmoqlarni ko‘rish uchun bosing"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Ulanish"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Barcha tarmoqlar"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"<xliff:g id="NAME">%s</xliff:g> ilovasi topgan Wi-Fi tarmoqqa ulanildi"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"<xliff:g id="NAME">%s</xliff:g> ilovasi sizga tarmoq taklif etishi uchun unga ruxsat berilsinmi?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Ha"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Yoʻq"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi avtomatik ravishda yoqiladi"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saqlangan tarmoqlar ichidan signali yaxshisi hududida ekaningizda"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Qayta yoqilmasin"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 70f3161..86bb532 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Cho phép ứng dụng truy cập vào các tính năng điện thoại của thiết bị. Quyền này cho phép ứng dụng xác định số điện thoại và ID thiết bị, cho dù cuộc gọi có hiện hoạt hay không và số từ xa có được kết nối bằng một cuộc gọi hay không."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"định tuyến cuộc gọi thông qua hệ thống"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Cho phép ứng dụng định tuyến cuộc gọi thông qua hệ thống nhằm cải thiện trải nghiệm gọi điện."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"xem và kiểm soát cuộc gọi thông qua hệ thống."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Cho phép ứng dụng xem và kiểm soát cuộc gọi đến trên thiết bị. Ứng dụng sẽ xem và kiểm soát các thông tin như số điện thoại của các cuộc gọi và trạng thái cuộc gọi."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"tiếp tục cuộc gọi từ một ứng dụng khác"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Cho phép ứng dụng tiếp tục cuộc gọi được bắt đầu trong một ứng dụng khác."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"đọc số điện thoại"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Cho phép ứng dụng giao tiếp với thẻ Giao tiếp trường gần (NFC), thẻ và trình đọc."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"vô hiệu hóa khóa màn hình của bạn"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Cho phép ứng dụng tắt khóa phím và bất kỳ bảo mật mật khẩu được liên kết nào. Ví dụ: điện thoại tắt khóa phím khi nhận được cuộc gọi đến, sau đó bật lại khóa phím khi cuộc gọi kết thúc."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"nhận và yêu cầu độ phức tạp của khóa màn hình"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Cho phép ứng dụng nắm được mức độ phức tạp của khóa màn hình (cao, trung bình, thấp hoặc không có), cho biết khoảng độ dài và loại khóa màn hình có thể có. Ứng dụng cũng có thể đề xuất cho người dùng rằng họ nên cập nhật khóa màn hình lên một mức độ nhất định nhưng người dùng có thể tùy ý bỏ qua và chuyển sang phần khác. Xin lưu ý rằng khóa màn hình không được lưu trữ dưới dạng văn bản thuần túy, do đó, ứng dụng sẽ không biết mật khẩu chính xác."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"sử dụng phần cứng sinh trắc học"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Cho phép ứng dụng dùng phần cứng sinh trắc học để xác thực"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"quản lý phần cứng vân tay"</string>
@@ -1083,22 +1087,22 @@
<string name="deleteText" msgid="6979668428458199034">"Xóa"</string>
<string name="inputMethod" msgid="1653630062304567879">"Phương thức nhập"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Tác vụ văn bản"</string>
- <string name="email" msgid="4560673117055050403">"Email"</string>
+ <string name="email" msgid="4560673117055050403">"Gửi email"</string>
<string name="email_desc" msgid="3638665569546416795">"Gửi email đến địa chỉ đã chọn"</string>
- <string name="dial" msgid="1253998302767701559">"Gọi"</string>
+ <string name="dial" msgid="1253998302767701559">"Gọi điện"</string>
<string name="dial_desc" msgid="6573723404985517250">"Gọi đến số điện thoại đã chọn"</string>
- <string name="map" msgid="5441053548030107189">"Bản đồ"</string>
+ <string name="map" msgid="5441053548030107189">"Xem bản đồ"</string>
<string name="map_desc" msgid="1836995341943772348">"Tìm địa chỉ đã chọn"</string>
<string name="browse" msgid="1245903488306147205">"Mở"</string>
<string name="browse_desc" msgid="8220976549618935044">"Mở URL đã chọn"</string>
- <string name="sms" msgid="4560537514610063430">"Gửi tin nhắn"</string>
+ <string name="sms" msgid="4560537514610063430">"Nhắn tin"</string>
<string name="sms_desc" msgid="7526588350969638809">"Nhắn tin đến số điện thoại đã chọn"</string>
<string name="add_contact" msgid="7867066569670597203">"Thêm"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"Thêm vào danh bạ"</string>
<string name="view_calendar" msgid="979609872939597838">"Xem"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Xem thời gian đã chọn trong lịch"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Lịch biểu"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Lên lịch biểu sự kiện cho thời gian đã chọn"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Lên lịch"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Lên lịch sự kiện cho thời gian đã chọn"</string>
<string name="view_flight" msgid="7691640491425680214">"Theo dõi"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Theo dõi chuyến bay đã chọn"</string>
<string name="translate" msgid="9218619809342576858">"Dịch"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Máy tính bảng đang cập nhật…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Thiết bị đang cập nhật…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Điện thoại đang khởi động…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android đang khởi động..."</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Máy tính bảng đang khởi động…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Thiết bị đang khởi động…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tối ưu hóa lưu trữ."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Nhấn để xem tất cả các mạng"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Kết nối"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Tất cả các mạng"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Đã kết nối với mạng Wi‑Fi do <xliff:g id="NAME">%s</xliff:g> đề xuất"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Bạn có muốn cho phép <xliff:g id="NAME">%s</xliff:g> đề xuất mạng cho bạn không?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Có"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Không"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi sẽ tự động bật"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Khi bạn ở gần mạng đã lưu chất lượng cao"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Không bật lại"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c38406f..dbb7834 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"允许该应用访问设备的电话功能。此权限可让该应用确定本机号码和设备 ID、是否正处于通话状态以及拨打的号码。"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"通过系统转接来电"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"允许该应用通过系统转接来电,以改善通话体验。"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"查看并控制通过系统拨打的电话。"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"允许应用查看并控制设备上正在进行的通话,其中包括通话号码和通话状态等信息。"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"继续进行来自其他应用的通话"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"允许该应用继续进行在其他应用中发起的通话。"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"读取电话号码"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"允许应用与近距离无线通信(NFC)标签、卡和读取器通信。"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"停用屏幕锁定"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"允许该应用停用键锁以及任何关联的密码安全措施。例如,让手机在接听来电时停用键锁,在通话结束后重新启用键锁。"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"获取和请求屏幕锁定复杂度"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"允许应用了解屏幕锁定复杂度(高、中、低或无),即屏幕锁定的可能长度范围和类型。应用还可以建议用户将屏幕锁定更新为特定复杂度,但用户可以随意选择忽略该建议并离开。请注意,系统不会以纯文字形式存储屏幕锁定选项的内容,因此应用不会知道确切密码。"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"使用生物特征硬件"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"允许该应用使用生物特征硬件进行身份验证"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"管理指纹硬件"</string>
@@ -1083,11 +1087,11 @@
<string name="deleteText" msgid="6979668428458199034">"删除"</string>
<string name="inputMethod" msgid="1653630062304567879">"输入法"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string>
- <string name="email" msgid="4560673117055050403">"电子邮件"</string>
+ <string name="email" msgid="4560673117055050403">"发送电子邮件"</string>
<string name="email_desc" msgid="3638665569546416795">"将电子邮件发送至所选地址"</string>
<string name="dial" msgid="1253998302767701559">"拨打电话"</string>
<string name="dial_desc" msgid="6573723404985517250">"拨打所选电话号码"</string>
- <string name="map" msgid="5441053548030107189">"地图"</string>
+ <string name="map" msgid="5441053548030107189">"查看地图"</string>
<string name="map_desc" msgid="1836995341943772348">"找到所选地址"</string>
<string name="browse" msgid="1245903488306147205">"打开"</string>
<string name="browse_desc" msgid="8220976549618935044">"打开所选网址"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"平板电脑正在更新…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"设备正在更新…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"手机正在启动…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android 正在启动…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"平板电脑正在启动…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"设备正在启动…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在优化存储空间。"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"点按即可查看所有网络"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"连接"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"所有网络"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"已连接到<xliff:g id="NAME">%s</xliff:g>建议的 WLAN 网络"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"要让<xliff:g id="NAME">%s</xliff:g>为您建议网络吗?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"是"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"否"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WLAN 将自动开启"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"当您位于已保存的高品质网络信号范围内时"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"不要重新开启"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index f59cfcb..6248db5 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"允許應用程式使用裝置的電話功能。這項權限允許應用程式確定手機號碼和裝置編號、是否正在通話中,以及所撥打的對方號碼。"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"透過系統轉接來電"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"允許應用程式透過系統轉接來電,以改善通話體驗。"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"透過系統查看和控制通話。"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"允許應用程式查看和控制裝置上正在進行的通話,當中包括通話號碼和通話狀態等資訊。"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"繼續進行來自其他應用程式的通話"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"允許應用程式繼續進行在其他應用程式中開始的通話。"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"讀取電話號碼"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"允許應用程式使用近距離無線通訊 (NFC) 標記、卡片及讀取程式進行通訊。"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"停用螢幕上鎖"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"允許應用程式停用按鍵鎖定以及其他相關的密碼安全措施。例如:手機收到來電時停用按鍵鎖定,通話結束後重新啟用按鍵鎖定。"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"獲取並要求螢幕鎖定複雜程度"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"允許應用程式瞭解螢幕鎖定的複雜程度 (高、中、低或無),這項資料說明了螢幕鎖定的可能長度範圍和類型。應用程式亦能建議使用者將螢幕鎖定更新至某個複雜程度,但使用者可以隨意忽略並離開。請注意,螢幕鎖定並非以純文字儲存,因此應用程式不知道確切的密碼。"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"使用生物識別硬件"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"允許應用程式使用生物識別硬件驗證"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"管理指紋硬件"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"正在更新平板電腦…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"正在更新裝置…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"手機正在啟動…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android 正在啟動…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"平板電腦正在啟動…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"裝置正在啟動…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在優化儲存空間。"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"輕按即可查看所有網絡"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"連線"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"所有網絡"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"已連接至「<xliff:g id="NAME">%s</xliff:g>」建議的 Wi‑Fi 網絡"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"您希望「<xliff:g id="NAME">%s</xliff:g>」為您建議網絡嗎?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"是"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"否"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi 將會自動開啟"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"當您位於已儲存的高品質網絡信號範圍內時"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"不要重新開啟"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 489dfea..74da200 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"允許應用程式使用裝置的電話功能。這項權限可讓應用程式判讀手機號碼和裝置 ID、是否正在通話中,以及所撥打的對方號碼。"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"透過系統接通來電"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"允許應用程式透過系統接通來電,以改善通話品質。"</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"查看及控管透過系統撥打的電話。"</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"允許應用程式查看及控管在裝置上撥出的電話,包括撥打的電話號碼和通話狀態等資訊。"</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"繼續進行來自其他應用程式的通話"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"允許應用程式繼續進行在其他應用程式中發起的通話。"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"讀取電話號碼"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"允許應用程式與近距離無線通訊 (NFC) 電子感應標籤、卡片及感應器進行通訊。"</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"停用螢幕鎖定"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"允許應用程式停用按鍵鎖定以及其他相關的密碼安全性功能。例如:手機收到來電時停用按鍵鎖定,通話結束後重新啟用按鍵鎖定。"</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"取得及要求螢幕鎖定複雜度"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"允許應用程式記住螢幕鎖定的複雜度 (高、中、低或無),即螢幕鎖定的可能長度範圍和類型。這樣一來,應用程式還能建議使用者將螢幕鎖定更新為特定複雜度,但使用者可選擇忽略建議並離開應用程式。請注意,系統不會以純文字格式儲存螢幕鎖定選項的內容,因此應用程式不會知道確切密碼。"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"使用生物特徵硬體"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"允許應用程式使用生物特徵硬體進行驗證"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"管理指紋硬體"</string>
@@ -1083,28 +1087,28 @@
<string name="deleteText" msgid="6979668428458199034">"刪除"</string>
<string name="inputMethod" msgid="1653630062304567879">"輸入法"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"文字動作"</string>
- <string name="email" msgid="4560673117055050403">"電子郵件"</string>
- <string name="email_desc" msgid="3638665569546416795">"將電子郵件寄到所選地址"</string>
- <string name="dial" msgid="1253998302767701559">"通話"</string>
- <string name="dial_desc" msgid="6573723404985517250">"撥打所選電話號碼"</string>
- <string name="map" msgid="5441053548030107189">"地圖"</string>
- <string name="map_desc" msgid="1836995341943772348">"尋找所選地址"</string>
+ <string name="email" msgid="4560673117055050403">"發送電子郵件"</string>
+ <string name="email_desc" msgid="3638665569546416795">"將電子郵件寄到選取的地址"</string>
+ <string name="dial" msgid="1253998302767701559">"撥號通話"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"撥打選取的電話號碼"</string>
+ <string name="map" msgid="5441053548030107189">"查看地圖"</string>
+ <string name="map_desc" msgid="1836995341943772348">"尋找選取的地址"</string>
<string name="browse" msgid="1245903488306147205">"開啟"</string>
- <string name="browse_desc" msgid="8220976549618935044">"開啟所選網址"</string>
- <string name="sms" msgid="4560537514610063430">"訊息"</string>
- <string name="sms_desc" msgid="7526588350969638809">"將訊息傳送到所選電話號碼"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"開啟選取的網址"</string>
+ <string name="sms" msgid="4560537514610063430">"發送訊息"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"將訊息傳送到選取的電話號碼"</string>
<string name="add_contact" msgid="7867066569670597203">"新增"</string>
<string name="add_contact_desc" msgid="4830217847004590345">"新增至聯絡人"</string>
<string name="view_calendar" msgid="979609872939597838">"查看"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"在日曆中查看所選時間"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"時間表"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"將活動安排在所選時間"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"在日曆中查看選取的時間"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"加入日曆"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"將活動安排在選取的時間"</string>
<string name="view_flight" msgid="7691640491425680214">"追蹤"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"追蹤所選航班"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"追蹤選取的航班"</string>
<string name="translate" msgid="9218619809342576858">"翻譯"</string>
- <string name="translate_desc" msgid="4502367770068777202">"翻譯所選文字"</string>
- <string name="define" msgid="7394820043869954211">"定義"</string>
- <string name="define_desc" msgid="7910883642444919726">"定義所選文字"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"翻譯選取的文字"</string>
+ <string name="define" msgid="7394820043869954211">"查看定義"</string>
+ <string name="define_desc" msgid="7910883642444919726">"定義選取的文字"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"平板電腦正在更新…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"裝置正在更新…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"手機正在啟動…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"Android 正在啟動…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"平板電腦正在啟動…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"裝置正在啟動…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在對儲存空間進行最佳化處理。"</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"輕觸即可查看所有網路"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"連線"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"所有網路"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"已連上「<xliff:g id="NAME">%s</xliff:g>」建議使用的 Wi-Fi 網路"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"是否要允許「<xliff:g id="NAME">%s</xliff:g>」為你建議網路?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"是"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"否"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi 將自動開啟"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"當你位於已儲存的高品質網路範圍內時"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"不要重新開啟"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 18a3db4..93b2ae6 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -447,6 +447,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ivumela uhlelo lokusebenza ukufinyelela izici zefoni zedivayisi. Le mvume ivumela uhlelo lokusebenza ukucacisa inombolo yefoni nobunikazi bedivayisi, ukuthi noma ikholi iyasebenza, futhi nenombolo yesilawuli kude zixhunywe ngekholi."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"yanza imizila yamakholi ngesistimu"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ivumela uhlelo lokusebenza ukwenza imizila yamakholi ngesistimu ukuze ithuthukise umuzwa wokushaya."</string>
+ <string name="permlab_callCompanionApp" msgid="3599252979411970473">"bona futhi ulawule amakholi ngesistimu."</string>
+ <string name="permdesc_callCompanionApp" msgid="4567344683275099090">"Ivumela uhlelo lokusebenza ukubona nokulawula amakholi aqhubekayo kudivayisi. Lokhu kubandakanya ulwazi olufana nezinombolo zamakholi nesimo samakholi."</string>
<string name="permlab_acceptHandover" msgid="2661534649736022409">"qhuba ikholi kusukela kolunye uhlelo lokusebenza"</string>
<string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ivumela uhlelo lokusebenza ukuze luqhube ikholi eqalwe kolunye uhlelo lokusebenza."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"funda izinombolo zefoni"</string>
@@ -507,6 +509,8 @@
<string name="permdesc_nfc" msgid="7120611819401789907">"Ivuela uhlelo lokusebenza ukuthi ixhumane ne-Near Field Communication (NFC) amathegi, amakhadi kanye nezinhlelo zokufunda."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"khubaza ukukhiya kwakho iskrini"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Ivumela uhlelo lokusebenza ukukhubaza ukuvala ukhiye nanoma yikuphi ukuphepha kwephasiwedi okuhlobene. Isibonelo, ifoni ikhubaza ukuvala ukhiye lapho ithola ikholi yefoni engenayo, bese inike amandla kabusha ukuvala ukhiye lapho ikholi isiqedile."</string>
+ <string name="permlab_getAndRequestScreenLockComplexity" msgid="3911592334192005424">"thola futhi ucele ukukhiywa kwesikrini"</string>
+ <string name="permdesc_getAndRequestScreenLockComplexity" msgid="9045202063228854873">"Ivumela uhlelo lokusebenza ukufunda ileveli yokukhiywa kwesikrini (phezulu, phakathi, phansi noma lutho), okubonisa ibanga elinokwenzeka lobubanzi nohlobo lokukhiywa kwesikrini. Uhlelo lokusebenza futhi lingaphakamisa kubasebenzisi ukuthi babuyekeze ukukhiywa kwesikrini kuya kwenye ileveli kodwa abasebenzisi bangaziba kalula bese bazule. Qaphela ukuthi ukhiywa kwesikrini akulondoloziwe embalweni ocacile ukuze uhlelo lokusebenza lingazi iphasiwedi."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"sebenzisa izingxenyekazi zekhompyutha ze-biometric"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Ivumela uhlelo lokusebenza ukuthi lusebenzise izingxenyekazi zekhompyutha ze-biometric ukuze kuqinisekiswe"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"phatha izingxenyekazi zekhompyutha zezigxivizo zeminwe"</string>
@@ -1097,7 +1101,7 @@
<string name="add_contact_desc" msgid="4830217847004590345">"Engeza koxhumana nabo"</string>
<string name="view_calendar" msgid="979609872939597838">"Buka"</string>
<string name="view_calendar_desc" msgid="5828320291870344584">"Buka isikhathi esikhethiwe kwikhalenda"</string>
- <string name="add_calendar_event" msgid="1953664627192056206">"Ishejuli"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Shejula"</string>
<string name="add_calendar_event_desc" msgid="4326891793260687388">"Shejula imicimbi yesikhathi esikhethiwe"</string>
<string name="view_flight" msgid="7691640491425680214">"Landelela"</string>
<string name="view_flight_desc" msgid="3876322502674253506">"Ithrekhi ikhethe indiza"</string>
@@ -1181,6 +1185,7 @@
<string name="android_upgrading_title" product="tablet" msgid="4503169817302593560">"Ithebulethi iyabuyekeza…"</string>
<string name="android_upgrading_title" product="device" msgid="7009520271220804517">"Idivayisi iyabuyekeza…"</string>
<string name="android_start_title" product="default" msgid="4536778526365907780">"Ifoni iyaqala…"</string>
+ <string name="android_start_title" product="automotive" msgid="8418054686415318207">"I-Android iyaqala…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"Ithebulethi iyaqala…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"Idivayisi iyaqala…"</string>
<string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ikhulisa isitoreji."</string>
@@ -1239,6 +1244,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Thepha ukuze ubone onke amanethiwekhi"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Xhuma"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Onke amanethiwekhi"</string>
+ <string name="wifi_suggestion_title" msgid="1524719325616630906">"Kuxhumeke kunethiwekhi ye-Wi‑Fi ephakanyiswe ngu-<xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="wifi_suggestion_content" msgid="4505530604935334589">"Ingabe ufuna ukuvumela i-<xliff:g id="NAME">%s</xliff:g> iphakamisa amanethiwkehi kuwe?"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yebo"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"Cha"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"I-Wi-Fi izovuleka ngokuzenzakalela"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Uma useduze kwenethiwekhi yekhwalithi ephezulu elondoloziwe"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ungaphindi uvule"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dd0b1ee..9f5eab5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2615,6 +2615,11 @@
<!-- Package name for default network scorer app; overridden by product overlays. -->
<string name="config_defaultNetworkScorerPackageName"></string>
+ <!-- Feature flag to enable memory efficient task snapshots that are used in recents optimized
+ for low memory devices and replace the app transition starting window with the splash
+ screen. -->
+ <bool name="config_lowRamTaskSnapshotsAndRecents">false</bool>
+
<!-- Determines whether recent tasks are provided to the user. Default device has recents
property. If this is false, then the following recents config flags are ignored. -->
<bool name="config_hasRecents">true</bool>
@@ -3492,8 +3497,6 @@
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
<string name="config_headlineFontFamily" translatable="false"></string>
- <!-- Name of a font family to use for headlines. Defaults to sans-serif-light -->
- <string name="config_headlineFontFamilyLight" translatable="false">sans-serif-light</string>
<!-- Allows setting custom fontFeatureSettings on specific text. -->
<string name="config_headlineFontFeatureSettings" translatable="false"></string>
@@ -3560,8 +3563,6 @@
<string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
<!-- Name of a font family to use for body text. -->
<string name="config_bodyFontFamily" translatable="false">sans-serif</string>
- <!-- Name of a font family to use for light body text. -->
- <string name="config_bodyFontFamilyLight" translatable="false">sans-serif-light</string>
<!-- Name of a font family to use for medium body text. -->
<string name="config_bodyFontFamilyMedium" translatable="false">sans-serif-medium</string>
@@ -3646,4 +3647,11 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string>
+
+ <!-- This is the default launcher component to use on secondary displays that support system
+ decorations.
+ This launcher activity must support multiple instances and have corresponding launch mode
+ set in AndroidManifest.
+ {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
+ <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
</resources>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 4b97fe75..7c95d1e 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -42,7 +42,7 @@
<item name="textColor">@color/btn_colored_text_material</item>
</style>
<style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView">
- <item name="textAppearance">@string/config_bodyFontFamily</item>
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
<style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
<style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/>
diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
index e6e0de3..5a9d3e6 100644
--- a/core/res/res/values/styles_permission_controller.xml
+++ b/core/res/res/values/styles_permission_controller.xml
@@ -25,15 +25,16 @@
</style>
<style name="PermissionGrantTitleIcon">
- <item name="layout_width">36dp</item>
- <item name="layout_height">36dp</item>
+ <item name="layout_width">24dp</item>
+ <item name="layout_height">24dp</item>
+ <item name="layout_marginBottom">12dp</item>
<item name="tint">?attr/colorAccent</item>
<item name="scaleType">fitCenter</item>
</style>
<style name="PermissionGrantTitleMessage"
parent="@style/TextAppearance.DeviceDefault">
- <item name="paddingStart">22dp</item>
+ <item name="gravity">center</item>
<item name="textSize">20sp</item>
<item name="textColor">?attr/textColorPrimary</item>
</style>
@@ -46,56 +47,22 @@
</style>
<style name="PermissionGrantDescription">
- <item name="layout_marginTop">20dp</item>
<item name="layout_marginStart">24dp</item>
- <item name="layout_marginBottom">16dp</item>
<item name="layout_marginEnd">24dp</item>
</style>
<style name="PermissionGrantContent">
- <item name="layout_marginStart">16dp</item>
+ <item name="layout_marginStart">24dp</item>
<item name="layout_marginEnd">24dp</item>
</style>
- <style name="PermissionGrantRadioGroup">
- <item name="layout_marginStart">8dp</item>
- <item name="layout_marginTop">-4dp</item>
- </style>
-
- <style name="PermissionGrantRadioButton"
- parent="@style/Widget.DeviceDefault.CompoundButton.RadioButton">
- <item name="paddingStart">16dp</item>
- <item name="paddingTop">8dp</item>
- <item name="paddingBottom">8dp</item>
- <item name="textSize">16sp</item>
- </style>
-
<style name="PermissionGrantDetailMessage"
parent="@style/TextAppearance.DeviceDefault">
- <item name="layout_marginStart">8dp</item>
- <item name="layout_marginBottom">4dp</item>
+ <item name="layout_marginTop">18dp</item>
<item name="textColor">?attr/textColorPrimary</item>
<item name="textSize">16sp</item>
</style>
- <style name="PermissionGrantDetailMessageSpace">
- <item name="layout_height">8dp</item>
- </style>
-
- <style name="PermissionGrantCheckbox"
- parent="@style/Widget.DeviceDefault.CompoundButton.CheckBox">
- <item name="paddingStart">16dp</item>
- <item name="textColor">?attr/textColorSecondary</item>
- <item name="buttonTint">?attr/textColorSecondary</item>
- <item name="textSize">16sp</item>
- </style>
-
- <style name="PermissionGrantButtonBar">
- <item name="layout_marginStart">24dp</item>
- <item name="layout_marginEnd">16dp</item>
- <item name="layout_marginBottom">4dp</item>
- </style>
-
<!-- styles for the permission review screen. -->
<style name="PermissionReviewDescription">
<item name="layout_marginTop">20dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 87fdc1f..e7cd227 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
/* Copyright 2012, The Android Open Source Project
**
@@ -330,6 +329,7 @@
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
+ <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
@@ -1369,15 +1369,6 @@
<java-symbol type="drawable" name="ic_wifi_signal_2" />
<java-symbol type="drawable" name="ic_wifi_signal_3" />
<java-symbol type="drawable" name="ic_wifi_signal_4" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_0_bars" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_1_bar" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_2_bars" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_3_bars" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_4_bars" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_4k" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_hd" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_sd" />
- <java-symbol type="drawable" name="ic_signal_wifi_badged_ld" />
<java-symbol type="drawable" name="stat_notify_rssi_in_range" />
<java-symbol type="drawable" name="stat_sys_gps_on" />
<java-symbol type="drawable" name="stat_sys_tether_wifi" />
@@ -3328,7 +3319,6 @@
<java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
<java-symbol type="string" name="config_headlineFontFamily" />
- <java-symbol type="string" name="config_headlineFontFamilyLight" />
<java-symbol type="string" name="config_headlineFontFamilyMedium" />
<java-symbol type="drawable" name="stat_sys_vitals" />
@@ -3522,4 +3512,7 @@
<java-symbol type="dimen" name="rounded_corner_radius_bottom" />
<java-symbol type="string" name="config_defaultModuleMetadataProvider" />
+
+ <!-- For Secondary Launcher -->
+ <java-symbol type="string" name="config_secondaryHomeComponent" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0ed8212..56265cc 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1442,7 +1442,7 @@
<style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
<!-- action bar -->
<item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
- <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
<item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
<!-- Color palette -->
@@ -1678,11 +1678,8 @@
<style name="ThemeOverlay.DeviceDefault" />
- <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for
- primary text -->
- <style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar">
- <item name="textColorPrimary">@color/btn_colored_borderless_text_material</item>
- </style>
+ <!-- @hide Theme overlay that inherits from material actionbar -->
+ <style name="ThemeOverlay.DeviceDefault.ActionBar" parent="ThemeOverlay.Material.ActionBar" />
<!-- @hide Theme overlay for a light popup in action bar -->
<style name="ThemeOverlay.DeviceDefault.Popup.Light" parent="@style/ThemeOverlay.Material.Light" />
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
index 369cee3..205c4eb 100644
--- a/core/res/res/values/themes_permission_controller.xml
+++ b/core/res/res/values/themes_permission_controller.xml
@@ -28,9 +28,6 @@
<style name="Theme.DeviceDefault.PermissionGrant"
parent="@style/Theme.DeviceDefault.Light.Dialog">
<item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
- <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
- <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
- <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
</style>
<!-- themes for the permission review dialog. -->
@@ -39,6 +36,5 @@
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
- <item name="buttonBarStyle">@style/PermissionReviewButtonBar</item>
</style>
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 6d1aae1..f8bd4e3 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -384,6 +384,7 @@
Settings.Global.PRIV_APP_OOB_LIST,
Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
new file mode 100644
index 0000000..d520f15
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.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 android.view;
+
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Insets;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+import android.view.SurfaceControl.Transaction;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsAnimationControlImplTest {
+
+ private InsetsAnimationControlImpl mController;
+
+ private SurfaceSession mSession = new SurfaceSession();
+ private SurfaceControl mTopLeash;
+ private SurfaceControl mNavLeash;
+
+ @Mock Transaction mMockTransaction;
+ @Mock InsetsController mMockController;
+ @Mock WindowInsetsAnimationControlListener mMockListener;
+ @Mock SyncRtSurfaceTransactionApplier mMockTransactionApplier;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTopLeash = new SurfaceControl.Builder(mSession)
+ .setName("testSurface")
+ .build();
+ mNavLeash = new SurfaceControl.Builder(mSession)
+ .setName("testSurface")
+ .build();
+ InsetsState state = new InsetsState();
+ state.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 500, 100));
+ state.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500));
+ InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, state,
+ () -> mMockTransaction, mMockController);
+ topConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mTopLeash));
+
+ InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(TYPE_NAVIGATION_BAR, state,
+ () -> mMockTransaction, mMockController);
+ navConsumer.hide();
+ navConsumer.setControl(new InsetsSourceControl(TYPE_NAVIGATION_BAR, mNavLeash));
+
+ SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
+ consumers.put(TYPE_TOP_BAR, topConsumer);
+ consumers.put(TYPE_NAVIGATION_BAR, navConsumer);
+ mController = new InsetsAnimationControlImpl(consumers,
+ new Rect(0, 0, 500, 500), state, mMockListener, WindowInsets.Type.systemBars(),
+ () -> mMockTransactionApplier);
+ }
+
+ @Test
+ public void testGetters() {
+ assertEquals(Insets.of(0, 100, 100, 0), mController.getShownStateInsets());
+ assertEquals(Insets.of(0, 0, 0, 0), mController.getHiddenStateInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), mController.getCurrentInsets());
+ assertEquals(WindowInsets.Type.systemBars(), mController.getTypes());
+ }
+
+ @Test
+ public void testChangeInsets() {
+ mController.changeInsets(Insets.of(0, 30, 40, 0));
+ assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
+
+ ArgumentCaptor<SurfaceParams> captor = ArgumentCaptor.forClass(SurfaceParams.class);
+ verify(mMockTransactionApplier).scheduleApply(captor.capture());
+ List<SurfaceParams> params = captor.getAllValues();
+ assertEquals(2, params.size());
+ SurfaceParams first = params.get(0);
+ SurfaceParams second = params.get(1);
+ SurfaceParams topParams = first.surface == mTopLeash ? first : second;
+ SurfaceParams navParams = first.surface == mNavLeash ? first : second;
+ assertPosition(topParams.matrix, new Rect(0, 0, 500, 100), new Rect(0, -70, 500, 30));
+ assertPosition(navParams.matrix, new Rect(400, 0, 500, 500), new Rect(460, 0, 560, 500));
+ }
+
+ private void assertPosition(Matrix m, Rect original, Rect transformed) {
+ RectF rect = new RectF(original);
+ rect.offsetTo(0, 0);
+ m.mapRect(rect);
+ rect.round(original);
+ assertEquals(original, transformed);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 2ad6028..d3d274a 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -28,6 +28,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
@Presubmit
@FlakyTest(detail = "Promote once confirmed non-flaky")
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 6bb9539..d41a718 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsState.INSET_SIDE_BOTTOM;
+import static android.view.InsetsState.INSET_SIDE_TOP;
import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
@@ -27,6 +29,7 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseIntArray;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,9 +48,12 @@
mState.getSource(TYPE_TOP_BAR).setVisible(true);
mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(TYPE_IME).setVisible(true);
+ SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT);
+ DisplayCutout.NO_CUTOUT, typeSideMap);
assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+ assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
+ assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
}
@Test
@@ -57,7 +63,7 @@
mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(TYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT);
+ DisplayCutout.NO_CUTOUT, null);
assertEquals(100, insets.getStableInsetBottom());
assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
}
@@ -69,7 +75,7 @@
mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT);
+ DisplayCutout.NO_CUTOUT, null);
assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
}
@@ -81,7 +87,7 @@
mState.getSource(TYPE_IME).setVisible(true);
mState.removeSource(TYPE_IME);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT);
+ DisplayCutout.NO_CUTOUT, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
index 8656781..c7b2dd1 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -20,7 +20,7 @@
LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_FLAGS := --no-resource-removal
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
index 06077a7..8b5fe99 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
android:label="Update Overlay Test"/>
</manifest>
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 86a8679..fef6320 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
@@ -19,9 +19,10 @@
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
index f95231f..8c00d14 100644
--- a/core/tests/packagemanagertests/Android.mk
+++ b/core/tests/packagemanagertests/Android.mk
@@ -9,7 +9,7 @@
$(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
frameworks-base-testutils \
mockito-target-minus-junit4
diff --git a/core/tests/packagemanagertests/AndroidManifest.xml b/core/tests/packagemanagertests/AndroidManifest.xml
index 8f49008..ee4a775 100644
--- a/core/tests/packagemanagertests/AndroidManifest.xml
+++ b/core/tests/packagemanagertests/AndroidManifest.xml
@@ -24,7 +24,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.coretests.packagemanager"
android:label="Frameworks PackageManager Core Tests" />
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
index 4e0f2a8..c5c9700 100644
--- a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -22,10 +22,11 @@
import android.os.FileUtils;
import android.os.ServiceManager;
import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 3c1526b..7765977 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests androidx.test.rules truth-prebuilt
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
diff --git a/core/tests/privacytests/AndroidManifest.xml b/core/tests/privacytests/AndroidManifest.xml
index a0e5281..3c82614 100644
--- a/core/tests/privacytests/AndroidManifest.xml
+++ b/core/tests/privacytests/AndroidManifest.xml
@@ -22,7 +22,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.coretests.privacy"
android:label="Frameworks Privacy Library Tests" />
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index c88a722..18928eb 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -17,21 +17,20 @@
package android.privacy;
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig;
import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index 71bd8f1..4a35350 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -22,8 +22,9 @@
import android.privacy.internal.rappor.RapporConfig;
import android.privacy.internal.rappor.RapporEncoder;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 5c60c81..343c07a 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -15,7 +15,7 @@
LOCAL_JNI_SHARED_LIBRARIES := libmemoryintarraytest libcutils libc++
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
frameworks-base-testutils \
mockito-target-minus-junit4 \
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
index 8db81ca..4ef4b1f 100644
--- a/core/tests/utiltests/AndroidManifest.xml
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -51,7 +51,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.utiltests"
android:label="Frameworks Utility Tests" />
diff --git a/core/tests/utiltests/runtests.sh b/core/tests/utiltests/runtests.sh
index 853119f..3b46cbb 100755
--- a/core/tests/utiltests/runtests.sh
+++ b/core/tests/utiltests/runtests.sh
@@ -21,4 +21,4 @@
adb install -r -g "$OUT/data/app/FrameworksUtilTests/FrameworksUtilTests.apk"
-adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/androidx.test.runner.AndroidJUnitRunner'
diff --git a/core/tests/utiltests/src/android/util/IntArrayTest.java b/core/tests/utiltests/src/android/util/IntArrayTest.java
index a6120a1..a76c640 100644
--- a/core/tests/utiltests/src/android/util/IntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/IntArrayTest.java
@@ -19,8 +19,9 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/src/android/util/LongArrayTest.java b/core/tests/utiltests/src/android/util/LongArrayTest.java
index a7afcbd..a9a168b 100644
--- a/core/tests/utiltests/src/android/util/LongArrayTest.java
+++ b/core/tests/utiltests/src/android/util/LongArrayTest.java
@@ -19,8 +19,9 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 24b33ef..2daefe7 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -24,8 +24,11 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
+
import libcore.io.IoUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/src/android/util/RemoteIntArray.java b/core/tests/utiltests/src/android/util/RemoteIntArray.java
index 11d0888..4a3a01e 100644
--- a/core/tests/utiltests/src/android/util/RemoteIntArray.java
+++ b/core/tests/utiltests/src/android/util/RemoteIntArray.java
@@ -24,7 +24,8 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
import java.io.Closeable;
import java.io.IOException;
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index b18ee17..03cf3eb 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -29,11 +29,12 @@
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
new file mode 100644
index 0000000..25dabad
--- /dev/null
+++ b/data/etc/Android.bp
@@ -0,0 +1,64 @@
+// 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.
+
+
+// Sysconfig files
+
+prebuilt_etc {
+ name: "framework-sysconfig.xml",
+ sub_dir: "sysconfig",
+ src: "framework-sysconfig.xml",
+}
+
+prebuilt_etc {
+ name: "hiddenapi-package-whitelist.xml",
+ sub_dir: "sysconfig",
+ src: "hiddenapi-package-whitelist.xml",
+}
+
+// Privapp permission whitelist files
+
+prebuilt_etc {
+ name: "platform.xml",
+ sub_dir: "permissions",
+ src: "platform.xml",
+}
+
+prebuilt_etc {
+ name: "privapp-permissions-platform.xml",
+ sub_dir: "permissions",
+ src: "privapp-permissions-platform.xml",
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.settings",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.settings.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.systemui",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.systemui.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "com.android.timezone.updater.xml",
+ sub_dir: "permissions",
+ src: "com.android.timezone.updater.xml",
+}
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
deleted file mode 100644
index ff8c4f1..0000000
--- a/data/etc/Android.mk
+++ /dev/null
@@ -1,78 +0,0 @@
-#
-# Copyright (C) 2008 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.
-#
-
-LOCAL_PATH := $(my-dir)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := framework-sysconfig.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp-permissions-platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := hiddenapi-package-whitelist.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp_whitelist_com.android.settings
-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)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp_whitelist_com.android.systemui
-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)
-
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.timezone.updater.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c216425..58b57e5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,6 +33,10 @@
<permission name="android.permission.CRYPT_KEEPER"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.carrierconfig">
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.cellbroadcastreceiver">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index c84c035..6a40792 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -319,9 +319,8 @@
<font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
</family>
<family lang="und-Mymr" variant="compact">
- <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
- <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
- <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
+ <font weight="400" style="normal">NotoSansMyanmarUI-Regular-ZawDecode.ttf</font>
+ <font weight="700" style="normal">NotoSansMyanmarUI-Bold-ZawDecode.ttf</font>
</family>
<family lang="und-Thaa">
<font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
@@ -365,7 +364,7 @@
<font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
</family>
<family lang="und-Cakm">
- <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
</family>
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 6798ab2..63a806e 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -556,7 +556,7 @@
* @hide
*/
public int saveUnclippedLayer(int left, int top, int right, int bottom) {
- return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, 0, 0);
+ return nSaveUnclippedLayer(mNativeCanvasWrapper, left, top, right, bottom);
}
/**
@@ -1395,6 +1395,8 @@
private static native int nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b,
int alpha, int layerFlags);
@CriticalNative
+ private static native int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b);
+ @CriticalNative
private static native boolean nRestore(long canvasHandle);
@CriticalNative
private static native void nRestoreToCount(long canvasHandle, int saveCount);
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index d9da27c..8258b57 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -93,6 +93,41 @@
}
/**
+ * Subtract two Insets.
+ *
+ * @param a The minuend.
+ * @param b The subtrahend.
+ * @return a - b, i. e. all insets on every side are subtracted from each other.
+ */
+ public static @NonNull Insets subtract(@NonNull Insets a, @NonNull Insets b) {
+ return Insets.of(a.left - b.left, a.top - b.top, a.right - b.right, a.bottom - b.bottom);
+ }
+
+ /**
+ * Retrieves the maximum of two Insets.
+ *
+ * @param a The first Insets.
+ * @param b The second Insets.
+ * @return max(a, b), i. e. the larger of every inset on every side is taken for the result.
+ */
+ public static @NonNull Insets max(@NonNull Insets a, @NonNull Insets b) {
+ return Insets.of(Math.max(a.left, b.left), Math.max(a.top, b.top),
+ Math.max(a.right, b.right), Math.max(a.bottom, b.bottom));
+ }
+
+ /**
+ * Retrieves the minimum of two Insets.
+ *
+ * @param a The first Insets.
+ * @param b The second Insets.
+ * @return min(a, b), i. e. the smaller of every inset on every side is taken for the result.
+ */
+ public static @NonNull Insets min(@NonNull Insets a, @NonNull Insets b) {
+ return Insets.of(Math.min(a.left, b.left), Math.min(a.top, b.top),
+ Math.min(a.right, b.right), Math.min(a.bottom, b.bottom));
+ }
+
+ /**
* Two Insets instances are equal iff they belong to the same class and their fields are
* pairwise equal.
*
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6e6ed30..9361c7c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -55,6 +55,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -315,13 +316,14 @@
}
/**
- * List uids of all keys that are auth bound to the current user.
+ * List uids of all keys that are auth bound to the current user.
* Only system is allowed to call this method.
*/
@UnsupportedAppUsage
public int[] listUidsOfAuthBoundKeys() {
- final int MAX_RESULT_SIZE = 100;
- int[] uidsOut = new int[MAX_RESULT_SIZE];
+ // uids are returned as a list of strings because list of integers
+ // as an output parameter is not supported by aidl-cpp.
+ List<String> uidsOut = new ArrayList<>();
try {
int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut);
if (rc != NO_ERROR) {
@@ -335,8 +337,8 @@
Log.w(TAG, "KeyStore exception", e);
return null;
}
- // Remove any 0 entries
- return Arrays.stream(uidsOut).filter(x -> x > 0).toArray();
+ // Turn list of strings into an array of uid integers.
+ return uidsOut.stream().mapToInt(Integer::parseInt).toArray();
}
public String[] list(String prefix) {
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 98af3eb..eeaefc5 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -167,6 +167,7 @@
},
},
data: ["tests/data/**/*.apk"],
+ test_suites: ["device-tests"],
}
cc_benchmark {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c20c720..5a26780 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -623,7 +623,7 @@
}
// Add the pairing of overlayable properties to resource ids to the package
- OverlayableInfo overlayable_info;
+ OverlayableInfo overlayable_info{};
overlayable_info.policy_flags = policy_header->policy_flags;
loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
break;
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 04cf611..bd1e6c5 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -18,6 +18,7 @@
X(Save)
X(Restore)
X(SaveLayer)
+X(SaveBehind)
X(Concat)
X(SetMatrix)
X(Translate)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 0b847af..4de25f9 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -18,6 +18,7 @@
#include "VectorDrawable.h"
+#include "SkAndroidFrameworkUtils.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDrawShadowInfo.h"
@@ -116,6 +117,16 @@
clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags});
}
};
+struct SaveBehind final : Op {
+ static const auto kType = Type::SaveBehind;
+ SaveBehind(const SkRect* subset) {
+ if (subset) { this->subset = *subset; }
+ }
+ SkRect subset = kUnset;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ SkAndroidFrameworkUtils::SaveBehind(c, &subset);
+ }
+};
struct Concat final : Op {
static const auto kType = Type::Concat;
@@ -579,6 +590,10 @@
this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags);
}
+void DisplayListData::saveBehind(const SkRect* subset) {
+ this->push<SaveBehind>(0, subset);
+}
+
void DisplayListData::concat(const SkMatrix& matrix) {
this->push<Concat>(0, matrix);
}
@@ -848,6 +863,11 @@
fDL->restore();
}
+bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) {
+ fDL->saveBehind(subset);
+ return false;
+}
+
void RecordingCanvas::didConcat(const SkMatrix& matrix) {
fDL->concat(matrix);
}
@@ -912,10 +932,6 @@
fDL->drawAnnotation(rect, key, val);
}
-void RecordingCanvas::onDrawTextRSXform(const void* text, size_t bytes, const SkRSXform xform[],
- const SkRect* cull, const SkPaint& paint) {
- fDL->drawTextRSXform(text, bytes, xform, cull, paint);
-}
void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
fDL->drawTextBlob(blob, x, y, paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index de8777b..ae3c4f05 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -75,6 +75,7 @@
void save();
void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*,
const SkMatrix*, SkCanvas::SaveLayerFlags);
+ void saveBehind(const SkRect*);
void restore();
void concat(const SkMatrix&);
@@ -146,6 +147,7 @@
void willSave() override;
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
void willRestore() override;
+ bool onDoSaveBehind(const SkRect*) override;
void onFlush() override;
@@ -171,8 +173,6 @@
void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
- void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
- const SkPaint&) override;
void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6be7ef7..83b9e7f 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -24,6 +24,7 @@
#include "hwui/PaintFilter.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkAndroidFrameworkUtils.h>
#include <SkAnimatedImage.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
@@ -185,6 +186,11 @@
return this->saveLayer(left, top, right, bottom, nullptr, flags);
}
+int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
+}
+
class SkiaCanvas::Clip {
public:
Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24d9c08..4ab0a59 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -74,6 +74,7 @@
SaveFlags::Flags flags) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
SaveFlags::Flags flags) override;
+ virtual int saveUnclippedLayer(int left, int top, int right, int bottom) override;
virtual void getMatrix(SkMatrix* outMatrix) const override;
virtual void setMatrix(const SkMatrix& matrix) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 71814c3..4c5365d 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -196,6 +196,7 @@
SaveFlags::Flags flags) = 0;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
SaveFlags::Flags flags) = 0;
+ virtual int saveUnclippedLayer(int, int, int, int) = 0;
// Matrix
virtual void getMatrix(SkMatrix* outMatrix) const = 0;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 2b5d580..0eb526a 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -82,11 +82,6 @@
mOutput << mIdent << "drawDRRect" << std::endl;
}
- void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
- const SkPaint&) override {
- mOutput << mIdent << "drawTextRSXform" << std::endl;
- }
-
void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override {
mOutput << mIdent << "drawTextBlob" << std::endl;
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index f2906de..ff87313 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -30,7 +30,7 @@
class SkiaPipeline : public renderthread::IRenderPipeline {
public:
- SkiaPipeline(renderthread::RenderThread& thread);
+ explicit SkiaPipeline(renderthread::RenderThread& thread);
virtual ~SkiaPipeline();
TaskManager* getTaskManager() override;
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
index daa4c18..dc8420f 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -23,7 +23,7 @@
class SkiaProfileRenderer : public IProfileRenderer {
public:
- SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
+ explicit SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
void drawRects(const float* rects, int count, const SkPaint& paint) override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 02874c7..53ffc44 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -25,7 +25,7 @@
class SkiaVulkanPipeline : public SkiaPipeline {
public:
- SkiaVulkanPipeline(renderthread::RenderThread& thread);
+ explicit SkiaVulkanPipeline(renderthread::RenderThread& thread);
virtual ~SkiaVulkanPipeline() {}
renderthread::MakeCurrentResult makeCurrent() override;
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
index 74e48ce..5e892aa 100644
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
@@ -62,8 +62,8 @@
public:
enum class StorageMode { allowSharedSurface, disallowSharedSurface };
- VectorDrawableAtlas(size_t surfaceArea,
- StorageMode storageMode = StorageMode::allowSharedSurface);
+ explicit VectorDrawableAtlas(size_t surfaceArea,
+ StorageMode storageMode = StorageMode::allowSharedSurface);
/**
* "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 35fc91a..66f04f1 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -59,7 +59,7 @@
private:
friend class RenderThread;
- CacheManager(const DisplayInfo& display);
+ explicit CacheManager(const DisplayInfo& display);
void reset(sk_sp<GrContext> grContext);
void destroy();
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 69ca23a..9eb942c 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -160,6 +160,7 @@
fPtr = ptr;
return *this;
}
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator FNPTR_TYPE() const { return fPtr; }
private:
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 146662b..1723c2e 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -30,10 +30,6 @@
void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
ADD_FAILURE() << "onDrawDRRect not expected in this test";
}
- void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
- const SkRect* cullRect, const SkPaint& paint) {
- ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
- }
void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) {
ADD_FAILURE() << "onDrawTextBlob not expected in this test";
}
@@ -116,4 +112,4 @@
int mDrawCounter = 0; // counts how may draw calls of any kind were made to this canvas
};
-}
\ No newline at end of file
+}
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index c56f689..ee1e33c 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -40,7 +40,7 @@
class IncidentReportArgs : public Parcelable {
public:
IncidentReportArgs();
- explicit IncidentReportArgs(const IncidentReportArgs& that);
+ IncidentReportArgs(const IncidentReportArgs& that);
virtual ~IncidentReportArgs();
virtual status_t writeToParcel(Parcel* out) const;
diff --git a/location/java/android/location/GnssMeasurementCallbackTransport.java b/location/java/android/location/GnssMeasurementCallbackTransport.java
index 21f6306..1188b13b 100644
--- a/location/java/android/location/GnssMeasurementCallbackTransport.java
+++ b/location/java/android/location/GnssMeasurementCallbackTransport.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.os.RemoteException;
+import com.android.internal.util.Preconditions;
+
/**
* A handler class to manage transport callbacks for {@link GnssMeasurementsEvent.Callback}.
*
@@ -26,12 +28,13 @@
*/
class GnssMeasurementCallbackTransport
extends LocalListenerHelper<GnssMeasurementsEvent.Callback> {
+ private static final String TAG = "GnssMeasCbTransport";
private final ILocationManager mLocationManager;
private final IGnssMeasurementsListener mListenerTransport = new ListenerTransport();
public GnssMeasurementCallbackTransport(Context context, ILocationManager locationManager) {
- super(context, "GnssMeasurementListenerTransport");
+ super(context, TAG);
mLocationManager = locationManager;
}
@@ -47,17 +50,34 @@
mLocationManager.removeGnssMeasurementsListener(mListenerTransport);
}
+ /**
+ * Injects GNSS measurement corrections into the GNSS chipset.
+ *
+ * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+ * measurement corrections to be injected into the GNSS chipset.
+ */
+ protected void injectGnssMeasurementCorrections(
+ GnssMeasurementCorrections measurementCorrections) throws RemoteException {
+ Preconditions.checkNotNull(measurementCorrections);
+ mLocationManager.injectGnssMeasurementCorrections(
+ measurementCorrections, getContext().getPackageName());
+ }
+
+ protected int getGnssCapabilities() throws RemoteException {
+ return mLocationManager.getGnssCapabilities(getContext().getPackageName());
+ }
+
private class ListenerTransport extends IGnssMeasurementsListener.Stub {
@Override
public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
ListenerOperation<GnssMeasurementsEvent.Callback> operation =
new ListenerOperation<GnssMeasurementsEvent.Callback>() {
- @Override
- public void execute(GnssMeasurementsEvent.Callback callback)
- throws RemoteException {
- callback.onGnssMeasurementsReceived(event);
- }
- };
+ @Override
+ public void execute(GnssMeasurementsEvent.Callback callback)
+ throws RemoteException {
+ callback.onGnssMeasurementsReceived(event);
+ }
+ };
foreach(operation);
}
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/location/java/android/location/GnssMeasurementCorrections.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to location/java/android/location/GnssMeasurementCorrections.aidl
index b7a7920..b05eb54 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/location/java/android/location/GnssMeasurementCorrections.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 208 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.hardware.usb;
+package android.location;
-parcelable UsbPort;
+parcelable GnssMeasurementCorrections;
diff --git a/location/java/android/location/GnssMeasurementCorrections.java b/location/java/android/location/GnssMeasurementCorrections.java
new file mode 100644
index 0000000..b81bf90
--- /dev/null
+++ b/location/java/android/location/GnssMeasurementCorrections.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class representing a GNSS measurement corrections for all used GNSS satellites at the location
+ * and time specified
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssMeasurementCorrections implements Parcelable {
+
+ /** Represents latitude in degrees at which the corrections are computed. */
+ private double mLatitudeDegrees;
+ /** Represents longitude in degrees at which the corrections are computed. */
+ private double mLongitudeDegrees;
+ /**
+ * Represents altitude in meters above the WGS 84 reference ellipsoid at which the corrections
+ * are computed.
+ */
+ private double mAltitudeMeters;
+
+ /** Time Of Applicability, GPS time of week */
+ private long mToaGpsNanosecondsOfWeek;
+
+ /**
+ * A set of {@link GnssSingleSatCorrection} each containing measurement corrections for a
+ * satellite in view
+ */
+ private @Nullable List<GnssSingleSatCorrection> mSingleSatCorrectionList;
+
+ private GnssMeasurementCorrections(Builder builder) {
+ mLatitudeDegrees = builder.mLatitudeDegrees;
+ mLongitudeDegrees = builder.mLongitudeDegrees;
+ mAltitudeMeters = builder.mAltitudeMeters;
+ mToaGpsNanosecondsOfWeek = builder.mToaGpsNanosecondsOfWeek;
+ mSingleSatCorrectionList =
+ builder.mSingleSatCorrectionList == null
+ ? null
+ : Collections.unmodifiableList(
+ new ArrayList<>(builder.mSingleSatCorrectionList));
+ }
+
+ /** Gets the latitude in degrees at which the corrections are computed. */
+ public double getLatitudeDegrees() {
+ return mLatitudeDegrees;
+ }
+
+ /** Gets the longitude in degrees at which the corrections are computed. */
+ public double getLongitudeDegrees() {
+ return mLongitudeDegrees;
+ }
+
+ /**
+ * Gets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections are
+ * computed.
+ */
+ public double getAltitudeMeters() {
+ return mAltitudeMeters;
+ }
+
+ /** Gets the time of applicability, GPS time of week in nanoseconds. */
+ public long getToaGpsNanosecondsOfWeek() {
+ return mToaGpsNanosecondsOfWeek;
+ }
+
+ /**
+ * Gets a set of {@link GnssSingleSatCorrection} each containing measurement corrections for a
+ * satellite in view
+ */
+ public @Nullable List<GnssSingleSatCorrection> getSingleSatCorrectionList() {
+ return mSingleSatCorrectionList;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GnssMeasurementCorrections> CREATOR =
+ new Creator<GnssMeasurementCorrections>() {
+ @Override
+ public GnssMeasurementCorrections createFromParcel(Parcel parcel) {
+ GnssMeasurementCorrections.Builder gnssMeasurementCorrectons =
+ new Builder()
+ .setLatitudeDegrees(parcel.readDouble())
+ .setLongitudeDegrees(parcel.readDouble())
+ .setAltitudeMeters(parcel.readDouble())
+ .setToaGpsNanosecondsOfWeek(parcel.readLong());
+ List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>();
+ parcel.readTypedList(singleSatCorrectionList, GnssSingleSatCorrection.CREATOR);
+ gnssMeasurementCorrectons.setSingleSatCorrectionList(
+ singleSatCorrectionList.isEmpty() ? null : singleSatCorrectionList);
+ return gnssMeasurementCorrectons.build();
+ }
+
+ @Override
+ public GnssMeasurementCorrections[] newArray(int i) {
+ return new GnssMeasurementCorrections[i];
+ }
+ };
+
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ StringBuilder builder = new StringBuilder("GnssMeasurementCorrections:\n");
+ builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees));
+ builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees));
+ builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters));
+ builder.append(
+ String.format(format, "ToaGpsNanosecondsOfWeek = ", mToaGpsNanosecondsOfWeek));
+ builder.append(
+ String.format(format, "mSingleSatCorrectionList = ", mSingleSatCorrectionList));
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeDouble(mLatitudeDegrees);
+ parcel.writeDouble(mLongitudeDegrees);
+ parcel.writeDouble(mAltitudeMeters);
+ parcel.writeLong(mToaGpsNanosecondsOfWeek);
+ parcel.writeTypedList(mSingleSatCorrectionList);
+ }
+
+ /** Builder for {@link GnssMeasurementCorrections} */
+ public static class Builder {
+ /**
+ * For documentation of below fields, see corresponding fields in {@link
+ * GnssMeasurementCorrections}.
+ */
+ private double mLatitudeDegrees;
+
+ private double mLongitudeDegrees;
+ private double mAltitudeMeters;
+ private long mToaGpsNanosecondsOfWeek;
+ private List<GnssSingleSatCorrection> mSingleSatCorrectionList;
+
+ /** Sets the latitude in degrees at which the corrections are computed. */
+ public Builder setLatitudeDegrees(double latitudeDegrees) {
+ mLatitudeDegrees = latitudeDegrees;
+ return this;
+ }
+
+ /** Sets the longitude in degrees at which the corrections are computed. */
+ public Builder setLongitudeDegrees(double longitudeDegrees) {
+ mLongitudeDegrees = longitudeDegrees;
+ return this;
+ }
+
+ /**
+ * Sets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections
+ * are computed.
+ */
+ public Builder setAltitudeMeters(double altitudeMeters) {
+ mAltitudeMeters = altitudeMeters;
+ return this;
+ }
+
+ /** Sets the time of applicability, GPS time of week in nanoseconds. */
+ public Builder setToaGpsNanosecondsOfWeek(long toaGpsNanosecondsOfWeek) {
+ mToaGpsNanosecondsOfWeek = toaGpsNanosecondsOfWeek;
+ return this;
+ }
+
+ /**
+ * Sets a the list of {@link GnssSingleSatCorrection} containing measurement corrections for
+ * a satellite in view
+ */
+ public Builder setSingleSatCorrectionList(
+ @Nullable List<GnssSingleSatCorrection> singleSatCorrectionList) {
+ if (singleSatCorrectionList == null) {
+ mSingleSatCorrectionList = null;
+ } else {
+ mSingleSatCorrectionList =
+ Collections.unmodifiableList(new ArrayList<>(singleSatCorrectionList));
+ }
+ return this;
+ }
+
+ /** Builds a {@link GnssMeasurementCorrections} instance as specified by this builder. */
+ public GnssMeasurementCorrections build() {
+ return new GnssMeasurementCorrections(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GnssReflectingPlane.java b/location/java/android/location/GnssReflectingPlane.java
new file mode 100644
index 0000000..64b3752
--- /dev/null
+++ b/location/java/android/location/GnssReflectingPlane.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Holds the characteristics of the reflecting plane that a satellite signal has bounced from.
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssReflectingPlane implements Parcelable {
+
+ /** Represents latitude in degrees of the reflecting plane */
+ private double mLatitudeDegrees;
+ /** Represents longitude in degrees of the reflecting plane. */
+ private double mLongitudeDegrees;
+ /**
+ * Represents altitude in meters above the WGS 84 reference ellipsoid of the reflection point in
+ * the plane
+ */
+ private double mAltitudeMeters;
+
+ /** Represents azimuth clockwise from north of the reflecting plane in degrees. */
+ private double mAzimuthDegrees;
+
+ private GnssReflectingPlane(Builder builder) {
+ mLatitudeDegrees = builder.mLatitudeDegrees;
+ mLongitudeDegrees = builder.mLongitudeDegrees;
+ mAltitudeMeters = builder.mAltitudeMeters;
+ mAzimuthDegrees = builder.mAzimuthDegrees;
+ }
+
+ /** Gets the latitude in degrees of the reflecting plane. */
+ public double getLatitudeDegrees() {
+ return mLatitudeDegrees;
+ }
+
+ /** Gets the longitude in degrees of the reflecting plane. */
+ public double getLongitudeDegrees() {
+ return mLongitudeDegrees;
+ }
+
+ /**
+ * Gets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point
+ * within the plane
+ */
+ public double getAltitudeMeters() {
+ return mAltitudeMeters;
+ }
+
+ /** Gets the azimuth clockwise from north of the reflecting plane in degrees. */
+ public double getAzimuthDegrees() {
+ return mAzimuthDegrees;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GnssReflectingPlane> CREATOR =
+ new Creator<GnssReflectingPlane>() {
+ @Override
+ public GnssReflectingPlane createFromParcel(Parcel parcel) {
+ GnssReflectingPlane reflectingPlane =
+ new Builder()
+ .setLatitudeDegrees(parcel.readDouble())
+ .setLongitudeDegrees(parcel.readDouble())
+ .setAltitudeMeters(parcel.readDouble())
+ .setAzimuthDegrees(parcel.readDouble())
+ .build();
+ return reflectingPlane;
+ }
+
+ @Override
+ public GnssReflectingPlane[] newArray(int i) {
+ return new GnssReflectingPlane[i];
+ }
+ };
+
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ StringBuilder builder = new StringBuilder("ReflectingPlane:\n");
+ builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees));
+ builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees));
+ builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters));
+ builder.append(String.format(format, "AzimuthDegrees = ", mAzimuthDegrees));
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeDouble(mLatitudeDegrees);
+ parcel.writeDouble(mLongitudeDegrees);
+ parcel.writeDouble(mAltitudeMeters);
+ parcel.writeDouble(mAzimuthDegrees);
+ }
+
+ /** Builder for {@link GnssReflectingPlane} */
+ public static class Builder {
+ /** For documentation, see corresponding fields in {@link GnssReflectingPlane}. */
+ private double mLatitudeDegrees;
+
+ private double mLongitudeDegrees;
+ private double mAltitudeMeters;
+ private double mAzimuthDegrees;
+
+ /** Sets the latitude in degrees of the reflecting plane. */
+ public Builder setLatitudeDegrees(double latitudeDegrees) {
+ mLatitudeDegrees = latitudeDegrees;
+ return this;
+ }
+
+ /** Sets the longitude in degrees of the reflecting plane. */
+ public Builder setLongitudeDegrees(double longitudeDegrees) {
+ mLongitudeDegrees = longitudeDegrees;
+ return this;
+ }
+
+ /**
+ * Sets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point
+ * within the plane
+ */
+ public Builder setAltitudeMeters(double altitudeMeters) {
+ mAltitudeMeters = altitudeMeters;
+ return this;
+ }
+
+ /** Sets the azimuth clockwise from north of the reflecting plane in degrees. */
+ public Builder setAzimuthDegrees(double azimuthDegrees) {
+ mAzimuthDegrees = azimuthDegrees;
+ return this;
+ }
+
+ /** Builds a {@link GnssReflectingPlane} object as specified by this builder. */
+ public GnssReflectingPlane build() {
+ return new GnssReflectingPlane(this);
+ }
+ }
+}
diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java
new file mode 100644
index 0000000..6c757f9
--- /dev/null
+++ b/location/java/android/location/GnssSingleSatCorrection.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A container with measurement corrections for a single visible satellite
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssSingleSatCorrection implements Parcelable {
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mSatIsLos}.
+ */
+ public static final int HAS_SAT_IS_LOS_MASK = 1 << 0;
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mExcessPathLengthMeters}.
+ */
+ public static final int HAS_EXCESS_PATH_LENGTH_MASK = 1 << 1;
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mExcessPathLengthUncertaintyMeters}.
+ */
+ public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 1 << 2;
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mReflectingPlane}.
+ */
+ public static final int HAS_REFLECTING_PLANE_MASK = 1 << 3;
+
+ /** A bitmask of fields present in this object (see HAS_* constants defined above) */
+ private int mSingleSatCorrectionFlags;
+
+ /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */
+ private int mConstellationType;
+
+ /**
+ * Satellite vehicle ID number
+ *
+ * <p>Interpretation depends on {@link GnssStatus#getSvid(int)}.
+ */
+ private int mSatId;
+
+ /**
+ * Carrier frequency of the signal to be corrected, for example it can be the GPS center
+ * frequency for L1 = 1,575,420,000 Hz, varying GLO channels, etc.
+ *
+ * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction
+ * objects will be reported for this same satellite, in one of the correction objects, all the
+ * values related to L1 will be filled, and in the other all of the values related to L5 will be
+ * filled.
+ */
+ private float mCarrierFrequencyHz;
+
+ /**
+ * True if the satellite is estimated to be in Line-of-Sight condition at the given location.
+ */
+ private boolean mSatIsLos;
+
+ /**
+ * Excess path length to be subtracted from pseudorange before using it in calculating location.
+ */
+ private float mExcessPathLengthMeters;
+
+ /** Error estimate (1-sigma) for the Excess path length estimate */
+ private float mExcessPathLengthUncertaintyMeters;
+
+ /**
+ * Defines the reflecting plane location and azimuth information
+ *
+ * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
+ * signal goes through multiple reflections or if reflection plane serving is not supported.
+ */
+ private @Nullable GnssReflectingPlane mReflectingPlane;
+
+ private GnssSingleSatCorrection(Builder builder) {
+ mSingleSatCorrectionFlags = builder.mSingleSatCorrectionFlags;
+ mSatId = builder.mSatId;
+ mConstellationType = builder.mConstellationType;
+ mCarrierFrequencyHz = builder.mCarrierFrequencyHz;
+ mSatIsLos = builder.mSatIsLos;
+ mExcessPathLengthMeters = builder.mExcessPathLengthMeters;
+ mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters;
+ mReflectingPlane = builder.mReflectingPlane;
+ }
+
+ /** Gets a bitmask of fields present in this object */
+ public int getSingleSatCorrectionFlags() {
+ return mSingleSatCorrectionFlags;
+ }
+
+ /**
+ * Gets the constellation type.
+ *
+ * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in {@link
+ * GnssStatus}.
+ */
+ @GnssStatus.ConstellationType
+ public int getConstellationType() {
+ return mConstellationType;
+ }
+
+ /**
+ * Gets the satellite ID.
+ *
+ * <p>Interpretation depends on {@link #getConstellationType()}. See {@link
+ * GnssStatus#getSvid(int)}.
+ */
+ public int getSatId() {
+ return mSatId;
+ }
+
+ /**
+ * Gets the carrier frequency of the tracked signal.
+ *
+ * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+ * L5 = 1176.45 MHz, varying GLO channels, etc.
+ *
+ * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction
+ * objects will be reported for this same satellite, in one of the correction objects, all the
+ * values related to L1 will be filled, and in the other all of the values related to L5 will be
+ * filled.
+ *
+ * @return the carrier frequency of the signal tracked in Hz.
+ */
+ public float getCarrierFrequencyHz() {
+ return mCarrierFrequencyHz;
+ }
+
+ /** True if the satellite is line-of-sight */
+ public boolean isSatelliteLineOfSight() {
+ return mSatIsLos;
+ }
+
+ /**
+ * Returns the Excess path length to be subtracted from pseudorange before using it in
+ * calculating location.
+ */
+ public float getExcessPathLengthMeters() {
+ return mExcessPathLengthMeters;
+ }
+
+ /** Returns the error estimate (1-sigma) for the Excess path length estimate */
+ public float getExcessPathLengthUncertaintyMeters() {
+ return mExcessPathLengthUncertaintyMeters;
+ }
+
+ /**
+ * Returns the reflecting plane characteristics at which the signal has bounced
+ *
+ * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
+ * signal goes through multiple reflections or if reflection plane serving is not supported
+ */
+ public @Nullable GnssReflectingPlane getReflectingPlane() {
+ return mReflectingPlane;
+ }
+
+ /** Returns {@code true} if {@link #isSatelliteLineOfSight()} is valid. */
+ public boolean hasSatelliteLineOfSight() {
+ return (mSingleSatCorrectionFlags & HAS_SAT_IS_LOS_MASK) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
+ public boolean hasExcessPathLength() {
+ return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */
+ public boolean hasExcessPathLengthUncertainty() {
+ return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getReflectingPlane()} is valid. */
+ public boolean hasReflectingPlane() {
+ return (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GnssSingleSatCorrection> CREATOR =
+ new Creator<GnssSingleSatCorrection>() {
+ @Override
+ public GnssSingleSatCorrection createFromParcel(Parcel parcel) {
+ GnssSingleSatCorrection singleSatCorrection =
+ new Builder()
+ .setSingleSatCorrectionFlags(parcel.readInt())
+ .setConstellationType(parcel.readInt())
+ .setSatId(parcel.readInt())
+ .setCarrierFrequencyHz(parcel.readFloat())
+ .setSatIsLos(parcel.readBoolean())
+ .setExcessPathLengthMeters(parcel.readFloat())
+ .setExcessPathLengthUncertaintyMeters(parcel.readFloat())
+ .setReflectingPlane(
+ GnssReflectingPlane.CREATOR.createFromParcel(parcel))
+ .build();
+ return singleSatCorrection;
+ }
+
+ @Override
+ public GnssSingleSatCorrection[] newArray(int i) {
+ return new GnssSingleSatCorrection[i];
+ }
+ };
+
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ StringBuilder builder = new StringBuilder("GnssSingleSatCorrection:\n");
+ builder.append(
+ String.format(format, "SingleSatCorrectionFlags = ", mSingleSatCorrectionFlags));
+ builder.append(String.format(format, "ConstellationType = ", mConstellationType));
+ builder.append(String.format(format, "SatId = ", mSatId));
+ builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz));
+ builder.append(String.format(format, "SatIsLos = ", mSatIsLos));
+ builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters));
+ builder.append(
+ String.format(
+ format,
+ "ExcessPathLengthUncertaintyMeters = ",
+ mExcessPathLengthUncertaintyMeters));
+ builder.append(String.format(format, "ReflectingPlane = ", mReflectingPlane));
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mSingleSatCorrectionFlags);
+ parcel.writeInt(mConstellationType);
+ parcel.writeInt(mSatId);
+ parcel.writeFloat(mCarrierFrequencyHz);
+ parcel.writeBoolean(mSatIsLos);
+ parcel.writeFloat(mExcessPathLengthMeters);
+ parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
+ mReflectingPlane.writeToParcel(parcel, flags);
+ }
+
+ /** Builder for {@link GnssSingleSatCorrection} */
+ public static class Builder {
+
+ /**
+ * For documentation of below fields, see corresponding fields in {@link
+ * GnssSingleSatCorrection}.
+ */
+ private int mSingleSatCorrectionFlags;
+
+ private int mConstellationType;
+ private int mSatId;
+ private float mCarrierFrequencyHz;
+ private boolean mSatIsLos;
+ private float mExcessPathLengthMeters;
+ private float mExcessPathLengthUncertaintyMeters;
+ private GnssReflectingPlane mReflectingPlane;
+
+ /** Sets a bitmask of fields present in this object */
+ public Builder setSingleSatCorrectionFlags(int singleSatCorrectionFlags) {
+ mSingleSatCorrectionFlags = singleSatCorrectionFlags;
+ return this;
+ }
+
+ /** Sets the constellation type. */
+ public Builder setConstellationType(int constellationType) {
+ mConstellationType = constellationType;
+ return this;
+ }
+
+ /** Sets the Satellite ID. */
+ public Builder setSatId(int satId) {
+ mSatId = satId;
+ return this;
+ }
+
+ /** Sets the Carrier frequency in Hz. */
+ public Builder setCarrierFrequencyHz(float carrierFrequencyHz) {
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ return this;
+ }
+
+ /** Sets the line=of-sight state of the satellite */
+ public Builder setSatIsLos(boolean satIsLos) {
+ mSatIsLos = satIsLos;
+ mSingleSatCorrectionFlags = (byte) (mSingleSatCorrectionFlags | HAS_SAT_IS_LOS_MASK);
+ return this;
+ }
+
+ /**
+ * Sets the Excess path length to be subtracted from pseudorange before using it in
+ * calculating location.
+ */
+ public Builder setExcessPathLengthMeters(float excessPathLengthMeters) {
+ mExcessPathLengthMeters = excessPathLengthMeters;
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_MASK);
+ return this;
+ }
+
+ /** Sets the error estimate (1-sigma) for the Excess path length estimate */
+ public Builder setExcessPathLengthUncertaintyMeters(
+ float excessPathLengthUncertaintyMeters) {
+ mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_UNC_MASK);
+ return this;
+ }
+
+ /** Sets the reflecting plane information */
+ public Builder setReflectingPlane(GnssReflectingPlane reflectingPlane) {
+ mReflectingPlane = reflectingPlane;
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags | HAS_REFLECTING_PLANE_MASK);
+ return this;
+ }
+
+ /** Builds a {@link GnssSingleSatCorrection} instance as specified by this builder. */
+ public GnssSingleSatCorrection build() {
+ return new GnssSingleSatCorrection(this);
+ }
+ }
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 05d49e5..bdc84da 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -21,6 +21,7 @@
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
+import android.location.GnssMeasurementCorrections;
import android.location.IBatchedLocationCallback;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssStatusListener;
@@ -63,6 +64,9 @@
boolean sendNiResponse(int notifId, int userResponse);
boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName);
+ void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
+ in String packageName);
+ int getGnssCapabilities(in String packageName);
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
boolean addGnssNavigationMessageListener(
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1cd3d86..040e4f9 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -2079,17 +2080,54 @@
}
/**
- * No-op method to keep backward-compatibility.
- * Don't use it. Use {@link #unregisterGnssMeasurementsCallback} instead.
+ * Injects GNSS measurement corrections into the GNSS chipset.
+ *
+ * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+ * measurement corrections to be injected into the GNSS chipset.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public void injectGnssMeasurementCorrections(
+ @NonNull GnssMeasurementCorrections measurementCorrections) {
+ try {
+ mGnssMeasurementCallbackTransport.injectGnssMeasurementCorrections(
+ measurementCorrections);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns an integer with flags representing the capabilities of the GNSS chipset.
+ *
+ * @hide
+ */
+ @SystemApi
+ /**
+ * Returns the integer capability flags of the GNSS chipset as defined in {@code
+ * IGnssCallback.hal}
+ */
+ public int getGnssCapabilities() {
+ try {
+ return mGnssMeasurementCallbackTransport.getGnssCapabilities();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * No-op method to keep backward-compatibility. Don't use it. Use {@link
+ * #unregisterGnssMeasurementsCallback} instead.
+ *
* @hide
* @deprecated use {@link #unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback)}
- * instead.
+ * instead.
*/
@Deprecated
@SystemApi
@SuppressLint("Doclava125")
- public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
- }
+ public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {}
/**
* Unregisters a GPS Measurement callback.
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 9bd5994..b531325 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -17,6 +17,7 @@
package com.android.internal.location;
import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
import android.app.Notification;
import android.app.NotificationManager;
@@ -27,19 +28,17 @@
import android.content.IntentFilter;
import android.location.LocationManager;
import android.location.INetInitiatedListener;
+import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.R;
import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.TelephonyProperties;
/**
* A GPS Network-initiated Handler class used by LocationManager.
@@ -50,8 +49,7 @@
private static final String TAG = "GpsNetInitiatedHandler";
- private static final boolean DEBUG = true;
- private static final boolean VERBOSE = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// NI verify activity for bringing up UI (not used yet)
public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
@@ -94,6 +92,9 @@
public static final int GPS_ENC_SUPL_UCS2 = 3;
public static final int GPS_ENC_UNKNOWN = -1;
+ // Limit on SUPL NI emergency mode time extension after emergency sessions ends
+ private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum
+
private final Context mContext;
private final TelephonyManager mTelephonyManager;
private final PhoneStateListener mPhoneStateListener;
@@ -109,7 +110,7 @@
private volatile boolean mIsSuplEsEnabled;
// Set to true if the phone is having emergency call.
- private volatile boolean mIsInEmergency;
+ private volatile boolean mIsInEmergencyCall;
// If Location function is enabled.
private volatile boolean mIsLocationEnabled = false;
@@ -119,6 +120,10 @@
// Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
static private boolean mIsHexInput = true;
+ // End time of emergency call, and extension, if set
+ private long mCallEndElapsedRealtimeMillis = 0;
+ private long mEmergencyExtensionMillis = 0;
+
public static class GpsNiNotification
{
public int notificationId;
@@ -146,16 +151,12 @@
if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
/*
- Emergency Mode is when during emergency call or in emergency call back mode.
- For checking if it is during emergency call:
- mIsInEmergency records if the phone is in emergency call or not. It will
+ Tracks the emergency call:
+ mIsInEmergencyCall records if the phone is in emergency call or not. It will
be set to true when the phone is having emergency call, and then will
be set to false by mPhoneStateListener when the emergency call ends.
- For checking if it is in emergency call back mode:
- Emergency call back mode will be checked by reading system properties
- when necessary: SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)
*/
- setInEmergency(PhoneNumberUtils.isEmergencyNumber(phoneNumber));
+ mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
} else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
updateLocationMode();
@@ -195,7 +196,10 @@
if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state);
// listening for emergency call ends
if (state == TelephonyManager.CALL_STATE_IDLE) {
- setInEmergency(false);
+ if (mIsInEmergencyCall) {
+ mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+ mIsInEmergencyCall = false;
+ }
}
}
};
@@ -229,22 +233,35 @@
return mIsLocationEnabled;
}
- // Note: Currently, there are two mechanisms involved to determine if a
- // phone is in emergency mode:
- // 1. If the user is making an emergency call, this is provided by activly
- // monitoring the outgoing phone number;
- // 2. If the device is in a emergency callback state, this is provided by
- // system properties.
- // If either one of above exists, the phone is considered in an emergency
- // mode. Because of this complexity, we need to be careful about how to set
- // and clear the emergency state.
- public void setInEmergency(boolean isInEmergency) {
- mIsInEmergency = isInEmergency;
+ /**
+ * Determines whether device is in user-initiated emergency session based on the following
+ * 1. If the user is making an emergency call, this is provided by actively
+ * monitoring the outgoing phone number;
+ * 2. If the user has recently ended an emergency call, and the device is in a configured time
+ * window after the end of that call.
+ * 3. If the device is in a emergency callback state, this is provided by querying
+ * TelephonyManager.
+ * @return true if is considered in user initiated emergency mode for NI purposes
+ */
+ public boolean getInEmergency() {
+ boolean isInEmergencyExtension =
+ (SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis) <
+ mEmergencyExtensionMillis;
+ boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+ return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension;
}
- public boolean getInEmergency() {
- boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
- return mIsInEmergency || isInEmergencyCallback;
+ public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
+ if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
+ Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+ + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
+ emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
+ } else if (emergencyExtensionSeconds < 0) {
+ Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+ + " is negative, reset to zero.");
+ emergencyExtensionSeconds = 0;
+ }
+ mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
}
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index b2fd8ec..3dcf694 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -12,7 +12,7 @@
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
core-test-rules \
guava \
mockito-target-minus-junit4 \
diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml
index ddb8ea6..5010d3d 100644
--- a/location/tests/locationtests/AndroidManifest.xml
+++ b/location/tests/locationtests/AndroidManifest.xml
@@ -29,7 +29,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.locationtests"
android:label="Frameworks Location Tests" />
</manifest>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
index bb6547b..7bddb58 100644
--- a/location/tests/locationtests/AndroidTest.xml
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="FrameworksLocationTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.locationtests" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
new file mode 100644
index 0000000..c18d58f
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.os.Parcel;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Unit tests for {@link GnssMeasurementCorrections}. */
+public class GnssMeasurementCorrectionsTest extends TestCase {
+ public void testDescribeContents() {
+ GnssMeasurementCorrections measurementCorrections =
+ new GnssMeasurementCorrections.Builder().build();
+ measurementCorrections.describeContents();
+ }
+
+ public void testWriteToParcel() {
+ GnssMeasurementCorrections.Builder measurementCorrections =
+ new GnssMeasurementCorrections.Builder();
+ setTestValues(measurementCorrections);
+ Parcel parcel = Parcel.obtain();
+ measurementCorrections.build().writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ GnssMeasurementCorrections newMeasurementCorrection =
+ GnssMeasurementCorrections.CREATOR.createFromParcel(parcel);
+ verifyTestValues(newMeasurementCorrection);
+ parcel.recycle();
+ }
+
+ private static void verifyTestValues(GnssMeasurementCorrections measurementCorrections) {
+ assertEquals(37.386051, measurementCorrections.getLatitudeDegrees());
+ assertEquals(-122.083855, measurementCorrections.getLongitudeDegrees());
+ assertEquals(32.0, measurementCorrections.getAltitudeMeters());
+ assertEquals(604000000000000L, measurementCorrections.getToaGpsNanosecondsOfWeek());
+
+ GnssSingleSatCorrection singleSatCorrection =
+ measurementCorrections.getSingleSatCorrectionList().get(0);
+ GnssSingleSatCorrectionsTest.verifyTestValues(singleSatCorrection);
+
+ singleSatCorrection = measurementCorrections.getSingleSatCorrectionList().get(1);
+ assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags());
+ assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType());
+ assertEquals(11, singleSatCorrection.getSatId());
+ assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz());
+ assertEquals(false, singleSatCorrection.isSatelliteLineOfSight());
+ assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters());
+ assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
+ GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
+ assertEquals(37.386054, reflectingPlane.getLatitudeDegrees());
+ assertEquals(-122.083855, reflectingPlane.getLongitudeDegrees());
+ assertEquals(120.0, reflectingPlane.getAltitudeMeters());
+ assertEquals(153.0, reflectingPlane.getAzimuthDegrees());
+ }
+
+ private static void setTestValues(GnssMeasurementCorrections.Builder measurementCorrections) {
+ measurementCorrections
+ .setLatitudeDegrees(37.386051)
+ .setLongitudeDegrees(-122.083855)
+ .setAltitudeMeters(32)
+ .setToaGpsNanosecondsOfWeek(604000000000000L);
+ List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>();
+ singleSatCorrectionList.add(GnssSingleSatCorrectionsTest.generateTestSingleSatCorrection());
+ singleSatCorrectionList.add(generateTestSingleSatCorrection());
+ measurementCorrections.setSingleSatCorrectionList(singleSatCorrectionList);
+ }
+
+ private static GnssSingleSatCorrection generateTestSingleSatCorrection() {
+ GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder();
+ singleSatCorrection
+ .setSingleSatCorrectionFlags(8)
+ .setConstellationType(GnssStatus.CONSTELLATION_GPS)
+ .setSatId(11)
+ .setCarrierFrequencyHz(1575430000f)
+ .setSatIsLos(false)
+ .setExcessPathLengthMeters(50.0f)
+ .setExcessPathLengthUncertaintyMeters(55.0f)
+ .setReflectingPlane(generateTestReflectingPlane());
+ return singleSatCorrection.build();
+ }
+
+ private static GnssReflectingPlane generateTestReflectingPlane() {
+ GnssReflectingPlane.Builder reflectingPlane =
+ new GnssReflectingPlane.Builder()
+ .setLatitudeDegrees(37.386054)
+ .setLongitudeDegrees(-122.083855)
+ .setAltitudeMeters(120.0)
+ .setAzimuthDegrees(153);
+ return reflectingPlane.build();
+ }
+}
diff --git a/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java
new file mode 100644
index 0000000..d7a3378
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.os.Parcel;
+
+import junit.framework.TestCase;
+
+/** Unit tests for {@link GnssReflectingPlane}. */
+public class GnssReflectingPlaneTest extends TestCase {
+ public void testDescribeContents() {
+ GnssReflectingPlane reflectingPlane = new GnssReflectingPlane.Builder().build();
+ reflectingPlane.describeContents();
+ }
+
+ public void testWriteToParcel() {
+ GnssReflectingPlane.Builder reflectingPlane = new GnssReflectingPlane.Builder();
+ setTestValues(reflectingPlane);
+ Parcel parcel = Parcel.obtain();
+ reflectingPlane.build().writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ GnssReflectingPlane newReflectingPlane =
+ GnssReflectingPlane.CREATOR.createFromParcel(parcel);
+ verifyTestValues(newReflectingPlane);
+ parcel.recycle();
+ }
+
+ public static void verifyTestValues(GnssReflectingPlane reflectingPlane) {
+ assertEquals(37.386052, reflectingPlane.getLatitudeDegrees());
+ assertEquals(-122.083853, reflectingPlane.getLongitudeDegrees());
+ assertEquals(100.0, reflectingPlane.getAltitudeMeters());
+ assertEquals(123.0, reflectingPlane.getAzimuthDegrees());
+ }
+
+ private static void setTestValues(GnssReflectingPlane.Builder reflectingPlane) {
+ GnssReflectingPlane refPlane = generateTestReflectingPlane();
+ reflectingPlane
+ .setLatitudeDegrees(refPlane.getLatitudeDegrees())
+ .setLongitudeDegrees(refPlane.getLongitudeDegrees())
+ .setAltitudeMeters(refPlane.getAltitudeMeters())
+ .setAzimuthDegrees(refPlane.getAzimuthDegrees());
+ }
+
+ public static GnssReflectingPlane generateTestReflectingPlane() {
+ GnssReflectingPlane.Builder reflectingPlane =
+ new GnssReflectingPlane.Builder()
+ .setLatitudeDegrees(37.386052)
+ .setLongitudeDegrees(-122.083853)
+ .setAltitudeMeters(100.0)
+ .setAzimuthDegrees(123.0);
+ return reflectingPlane.build();
+ }
+}
diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
new file mode 100644
index 0000000..2e54ae4
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.os.Parcel;
+
+import junit.framework.TestCase;
+
+/** Unit tests for {@link GnssSingleSatCorrection}. */
+public class GnssSingleSatCorrectionsTest extends TestCase {
+ public void testDescribeContents() {
+ GnssSingleSatCorrection singleSatCorrection = new GnssSingleSatCorrection.Builder().build();
+ singleSatCorrection.describeContents();
+ }
+
+ public void testWriteToParcel() {
+ GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder();
+ setTestValues(singleSatCorrection);
+ Parcel parcel = Parcel.obtain();
+ singleSatCorrection.build().writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ GnssSingleSatCorrection newSingleSatCorrection =
+ GnssSingleSatCorrection.CREATOR.createFromParcel(parcel);
+ verifyTestValues(newSingleSatCorrection);
+ parcel.recycle();
+ }
+
+ public static void verifyTestValues(GnssSingleSatCorrection singleSatCorrection) {
+ assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags());
+ assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType());
+ assertEquals(12, singleSatCorrection.getSatId());
+ assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz());
+ assertEquals(true, singleSatCorrection.isSatelliteLineOfSight());
+ assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters());
+ assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
+ GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
+ GnssReflectingPlaneTest.verifyTestValues(reflectingPlane);
+ }
+
+ private static void setTestValues(GnssSingleSatCorrection.Builder singleSatCorrection) {
+ GnssSingleSatCorrection singleSatCorr = generateTestSingleSatCorrection();
+ singleSatCorrection
+ .setSingleSatCorrectionFlags(singleSatCorr.getSingleSatCorrectionFlags())
+ .setConstellationType(singleSatCorr.getConstellationType())
+ .setSatId(singleSatCorr.getSatId())
+ .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz())
+ .setSatIsLos(singleSatCorr.isSatelliteLineOfSight())
+ .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters())
+ .setExcessPathLengthUncertaintyMeters(
+ singleSatCorr.getExcessPathLengthUncertaintyMeters())
+ .setReflectingPlane(singleSatCorr.getReflectingPlane());
+ }
+
+ public static GnssSingleSatCorrection generateTestSingleSatCorrection() {
+ GnssSingleSatCorrection.Builder singleSatCorrection =
+ new GnssSingleSatCorrection.Builder()
+ .setSingleSatCorrectionFlags(15)
+ .setConstellationType(GnssStatus.CONSTELLATION_GALILEO)
+ .setSatId(12)
+ .setCarrierFrequencyHz(1575420000f)
+ .setSatIsLos(true)
+ .setExcessPathLengthMeters(10.0f)
+ .setExcessPathLengthUncertaintyMeters(5.0f)
+ .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane());
+ return singleSatCorrection.build();
+ }
+}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 4b2353c..33f81f1 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,8 +16,10 @@
package android.media;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
@@ -43,6 +45,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* The AudioRecord class manages the audio resources for Java applications
@@ -58,7 +61,7 @@
* been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
* the total recording buffer size.
*/
-public class AudioRecord implements AudioRouting
+public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRecordingMonitorClient
{
//---------------------------------------------------------
// Constants
@@ -1654,6 +1657,56 @@
return activeMicrophones;
}
+
+ //--------------------------------------------------------------------------
+ // Implementation of AudioRecordingMonitor interface
+ //--------------------
+
+ AudioRecordingMonitorImpl mRecordingInfoImpl =
+ new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this);
+
+ /**
+ * Register a callback to be notified of audio capture changes via a
+ * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+ * configuration changes (pre-processing, format, sampling rate...) or capture is
+ * silenced/unsilenced by the system.
+ * @param executor {@link Executor} to handle the callbacks.
+ * @param cb non-null callback to register
+ */
+ public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AudioManager.AudioRecordingCallback cb) {
+ mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb);
+ }
+
+ /**
+ * Unregister an audio recording callback previously registered with
+ * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+ * @param cb non-null callback to unregister
+ */
+ public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
+ mRecordingInfoImpl.unregisterAudioRecordingCallback(cb);
+ }
+
+ /**
+ * Returns the current active audio recording for this audio recorder.
+ * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+ * or null otherwise.
+ * @see AudioRecordingConfiguration
+ */
+ public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
+ return mRecordingInfoImpl.getActiveRecordingConfiguration();
+ }
+
+ //---------------------------------------------------------
+ // Implementation of AudioRecordingMonitorClient interface
+ //--------------------
+ /**
+ * @hide
+ */
+ public int getPortId() {
+ return native_getPortId();
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 9ada216..de76aef 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,7 +18,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.media.audiofx.AudioEffect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -27,6 +29,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -48,7 +52,7 @@
public final class AudioRecordingConfiguration implements Parcelable {
private final static String TAG = new String("AudioRecordingConfiguration");
- private final int mSessionId;
+ private final int mClientSessionId;
private final int mClientSource;
@@ -60,18 +64,50 @@
private final int mPatchHandle;
+ private final int mClientPortId;
+
+ private boolean mClientSilenced;
+
+ private final int mDeviceSource;
+
+ private final AudioEffect.Descriptor[] mClientEffects;
+
+ private final AudioEffect.Descriptor[] mDeviceEffects;
+
/**
* @hide
*/
+ @TestApi
public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
- AudioFormat devFormat, int patchHandle, String packageName) {
+ AudioFormat devFormat, int patchHandle, String packageName, int clientPortId,
+ boolean clientSilenced, int deviceSource,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] deviceEffects) {
mClientUid = uid;
- mSessionId = session;
+ mClientSessionId = session;
mClientSource = source;
mClientFormat = clientFormat;
mDeviceFormat = devFormat;
mPatchHandle = patchHandle;
mClientPackageName = packageName;
+ mClientPortId = clientPortId;
+ mClientSilenced = clientSilenced;
+ mDeviceSource = deviceSource;
+ mClientEffects = clientEffects;
+ mDeviceEffects = deviceEffects;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public AudioRecordingConfiguration(int uid, int session, int source,
+ AudioFormat clientFormat, AudioFormat devFormat,
+ int patchHandle, String packageName) {
+ this(uid, session, source, clientFormat,
+ devFormat, patchHandle, packageName, 0 /*clientPortId*/,
+ false /*clientSilenced*/, MediaRecorder.AudioSource.DEFAULT /*deviceSource*/,
+ new AudioEffect.Descriptor[0] /*clientEffects*/,
+ new AudioEffect.Descriptor[0] /*deviceEffects*/);
}
/**
@@ -87,13 +123,26 @@
* @hide
*/
public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
- return new String("session:" + arc.mSessionId
- + " -- source:" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+ String clientEffects = new String();
+ for (AudioEffect.Descriptor desc : arc.mClientEffects) {
+ clientEffects += "'" + desc.name + "' ";
+ }
+ String deviceEffects = new String();
+ for (AudioEffect.Descriptor desc : arc.mDeviceEffects) {
+ deviceEffects += "'" + desc.name + "' ";
+ }
+
+ return new String("session:" + arc.mClientSessionId
+ + " -- source client=" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+ + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+ " -- uid:" + arc.mClientUid
+ " -- patch:" + arc.mPatchHandle
+ " -- pack:" + arc.mClientPackageName
+ " -- format client=" + arc.mClientFormat.toLogFriendlyString()
- + ", dev=" + arc.mDeviceFormat.toLogFriendlyString());
+ + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+ + " -- silenced:" + arc.mClientSilenced
+ + " -- effects client=" + clientEffects
+ + ", dev=" + deviceEffects);
}
// Note that this method is called server side, so no "privileged" information is ever sent
@@ -106,8 +155,10 @@
*/
public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) {
return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
- in.mSessionId, in.mClientSource, in.mClientFormat,
- in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/);
+ in.mClientSessionId, in.mClientSource, in.mClientFormat,
+ in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/,
+ in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
+ in.mDeviceEffects);
}
// matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source)
@@ -129,16 +180,8 @@
// documented return values match the sources that return false
// in MediaRecorder.isSystemOnlyAudioSource(source)
/**
- * Returns the audio source being used for the recording.
- * @return one of {@link MediaRecorder.AudioSource#DEFAULT},
- * {@link MediaRecorder.AudioSource#MIC},
- * {@link MediaRecorder.AudioSource#VOICE_UPLINK},
- * {@link MediaRecorder.AudioSource#VOICE_DOWNLINK},
- * {@link MediaRecorder.AudioSource#VOICE_CALL},
- * {@link MediaRecorder.AudioSource#CAMCORDER},
- * {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
- * {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
- * {@link MediaRecorder.AudioSource#UNPROCESSED}.
+ * Returns the audio source selected by the client.
+ * @return the audio source selected by the client.
*/
public @AudioSource int getClientAudioSource() { return mClientSource; }
@@ -146,7 +189,9 @@
* Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
* @return the session number.
*/
- public int getClientAudioSessionId() { return mSessionId; }
+ public int getClientAudioSessionId() {
+ return mClientSessionId;
+ }
/**
* Returns the audio format at which audio is recorded on this Android device.
@@ -223,6 +268,54 @@
return null;
}
+ /**
+ * Returns the system unique ID assigned for the AudioRecord object corresponding to this
+ * AudioRecordingConfiguration client.
+ * @return the port ID.
+ */
+ int getClientPortId() {
+ return mClientPortId;
+ }
+
+ /**
+ * Returns true if the audio returned to the client is currently being silenced by the
+ * audio framework due to concurrent capture policy (e.g the capturing application does not have
+ * an active foreground process or service anymore).
+ * @return true if captured audio is silenced, false otherwise .
+ */
+ public boolean isClientSilenced() {
+ return mClientSilenced;
+ }
+
+ /**
+ * Returns the audio source currently used to configure the capture path. It can be different
+ * from the source returned by {@link #getClientAudioSource()} if another capture is active.
+ * @return the audio source active on the capture path.
+ */
+ public @AudioSource int getAudioSource() {
+ return mDeviceSource;
+ }
+
+ /**
+ * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+ * the audio capture client (e.g. {@link AudioRecord} or {@link MediaRecorder}).
+ * @return List of {@link AudioEffect.Descriptor} containing all effects enabled for the client.
+ */
+ public @NonNull List<AudioEffect.Descriptor> getClientEffects() {
+ return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mClientEffects));
+ }
+
+ /**
+ * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+ * the capture stream.
+ * @return List of {@link AudioEffect.Descriptor} containing all effects enabled on the
+ * capture stream. This can be different from the list returned by {@link #getClientEffects()}
+ * if another capture is active.
+ */
+ public @NonNull List<AudioEffect.Descriptor> getEffects() {
+ return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mDeviceEffects));
+ }
+
public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
= new Parcelable.Creator<AudioRecordingConfiguration>() {
/**
@@ -240,7 +333,7 @@
@Override
public int hashCode() {
- return Objects.hash(mSessionId, mClientSource);
+ return Objects.hash(mClientSessionId, mClientSource);
}
@Override
@@ -250,23 +343,45 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mSessionId);
+ dest.writeInt(mClientSessionId);
dest.writeInt(mClientSource);
mClientFormat.writeToParcel(dest, 0);
mDeviceFormat.writeToParcel(dest, 0);
dest.writeInt(mPatchHandle);
dest.writeString(mClientPackageName);
dest.writeInt(mClientUid);
+ dest.writeInt(mClientPortId);
+ dest.writeBoolean(mClientSilenced);
+ dest.writeInt(mDeviceSource);
+ dest.writeInt(mClientEffects.length);
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mClientEffects[i].writeToParcel(dest, 0);
+ }
+ dest.writeInt(mDeviceEffects.length);
+ for (int i = 0; i < mDeviceEffects.length; i++) {
+ mDeviceEffects[i].writeToParcel(dest, 0);
+ }
}
private AudioRecordingConfiguration(Parcel in) {
- mSessionId = in.readInt();
+ mClientSessionId = in.readInt();
mClientSource = in.readInt();
mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
mPatchHandle = in.readInt();
mClientPackageName = in.readString();
mClientUid = in.readInt();
+ mClientPortId = in.readInt();
+ mClientSilenced = in.readBoolean();
+ mDeviceSource = in.readInt();
+ mClientEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mClientEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ }
+ mDeviceEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+ for (int i = 0; i < mClientEffects.length; i++) {
+ mDeviceEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ }
}
@Override
@@ -277,11 +392,16 @@
AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
return ((mClientUid == that.mClientUid)
- && (mSessionId == that.mSessionId)
+ && (mClientSessionId == that.mClientSessionId)
&& (mClientSource == that.mClientSource)
&& (mPatchHandle == that.mPatchHandle)
&& (mClientFormat.equals(that.mClientFormat))
&& (mDeviceFormat.equals(that.mDeviceFormat))
- && (mClientPackageName.equals(that.mClientPackageName)));
+ && (mClientPackageName.equals(that.mClientPackageName))
+ && (mClientPortId == that.mClientPortId)
+ && (mClientSilenced == that.mClientSilenced)
+ && (mDeviceSource == that.mDeviceSource)
+ && (mClientEffects.equals(that.mClientEffects))
+ && (mDeviceEffects.equals(that.mDeviceEffects)));
}
}
diff --git a/media/java/android/media/AudioRecordingMonitor.java b/media/java/android/media/AudioRecordingMonitor.java
new file mode 100644
index 0000000..e2605d0
--- /dev/null
+++ b/media/java/android/media/AudioRecordingMonitor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * AudioRecordingMonitor defines an interface implemented by {@link AudioRecord} and
+ * {@link MediaRecorder} allowing applications to install a callback and be notified of changes
+ * in the capture path while recoding is active.
+ */
+public interface AudioRecordingMonitor {
+ /**
+ * Register a callback to be notified of audio capture changes via a
+ * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+ * configuration changes (pre-processing, format, sampling rate...) or capture is
+ * silenced/unsilenced by the system.
+ * @param executor {@link Executor} to handle the callbacks.
+ * @param cb non-null callback to register
+ */
+ void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AudioManager.AudioRecordingCallback cb);
+
+ /**
+ * Unregister an audio recording callback previously registered with
+ * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+ * @param cb non-null callback to unregister
+ */
+ void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb);
+
+ /**
+ * Returns the current active audio recording for this audio recorder.
+ * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+ * or null otherwise.
+ * @see AudioRecordingConfiguration
+ */
+ @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration();
+}
diff --git a/media/java/android/media/AudioRecordingMonitorClient.java b/media/java/android/media/AudioRecordingMonitorClient.java
new file mode 100644
index 0000000..7578d9b
--- /dev/null
+++ b/media/java/android/media/AudioRecordingMonitorClient.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Interface implemented by classes using { @link AudioRecordingMonitor} interface.
+ * @hide
+ */
+public interface AudioRecordingMonitorClient {
+ /**
+ * @return the unique port ID allocated by audio framework to this recorder
+ */
+ int getPortId();
+}
diff --git a/media/java/android/media/AudioRecordingMonitorImpl.java b/media/java/android/media/AudioRecordingMonitorImpl.java
new file mode 100644
index 0000000..c2cd4bc
--- /dev/null
+++ b/media/java/android/media/AudioRecordingMonitorImpl.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of AudioRecordingMonitor interface.
+ * @hide
+ */
+public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
+
+ private static final String TAG = "android.media.AudioRecordingMonitor";
+
+ private static IAudioService sService; //lazy initialization, use getService()
+
+ private final AudioRecordingMonitorClient mClient;
+
+ AudioRecordingMonitorImpl(@NonNull AudioRecordingMonitorClient client) {
+ mClient = client;
+ }
+
+ /**
+ * Register a callback to be notified of audio capture changes via a
+ * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+ * configuration changes (pre-processing, format, sampling rate...) or capture is
+ * silenced/unsilenced by the system.
+ * @param executor {@link Executor} to handle the callbacks.
+ * @param cb non-null callback to register
+ */
+ public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AudioManager.AudioRecordingCallback cb) {
+ if (cb == null) {
+ throw new IllegalArgumentException("Illegal null AudioRecordingCallback");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Illegal null Executor");
+ }
+ synchronized (mRecordCallbackLock) {
+ // check if eventCallback already in list
+ for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
+ if (arci.mCb == cb) {
+ throw new IllegalArgumentException(
+ "AudioRecordingCallback already registered");
+ }
+ }
+ beginRecordingCallbackHandling();
+ mRecordCallbackList.add(new AudioRecordingCallbackInfo(executor, cb));
+ }
+ }
+
+ /**
+ * Unregister an audio recording callback previously registered with
+ * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+ * @param cb non-null callback to unregister
+ */
+ public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
+ if (cb == null) {
+ throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+ }
+
+ synchronized (mRecordCallbackLock) {
+ for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
+ if (arci.mCb == cb) {
+ // ok to remove while iterating over list as we exit iteration
+ mRecordCallbackList.remove(arci);
+ if (mRecordCallbackList.size() == 0) {
+ endRecordingCallbackHandling();
+ }
+ return;
+ }
+ }
+ throw new IllegalArgumentException("AudioRecordingCallback was not registered");
+ }
+ }
+
+ /**
+ * Returns the current active audio recording for this audio recorder.
+ * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+ * or null otherwise.
+ * @see AudioRecordingConfiguration
+ */
+ public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
+ final IAudioService service = getService();
+ try {
+ List<AudioRecordingConfiguration> configs = service.getActiveRecordingConfigurations();
+ return getMyConfig(configs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static class AudioRecordingCallbackInfo {
+ final AudioManager.AudioRecordingCallback mCb;
+ final Executor mExecutor;
+ AudioRecordingCallbackInfo(Executor e, AudioManager.AudioRecordingCallback cb) {
+ mExecutor = e;
+ mCb = cb;
+ }
+ }
+
+ private static final int MSG_RECORDING_CONFIG_CHANGE = 1;
+
+ private final Object mRecordCallbackLock = new Object();
+ @GuardedBy("mRecordCallbackLock")
+ @NonNull private LinkedList<AudioRecordingCallbackInfo> mRecordCallbackList =
+ new LinkedList<AudioRecordingCallbackInfo>();
+ @GuardedBy("mRecordCallbackLock")
+ private @Nullable HandlerThread mRecordingCallbackHandlerThread;
+ @GuardedBy("mRecordCallbackLock")
+ private @Nullable volatile Handler mRecordingCallbackHandler;
+
+ @GuardedBy("mRecordCallbackLock")
+ private final IRecordingConfigDispatcher mRecordingCallback =
+ new IRecordingConfigDispatcher.Stub() {
+ @Override
+ public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+ AudioRecordingConfiguration config = getMyConfig(configs);
+ if (config != null) {
+ synchronized (mRecordCallbackLock) {
+ if (mRecordingCallbackHandler != null) {
+ final Message m = mRecordingCallbackHandler.obtainMessage(
+ MSG_RECORDING_CONFIG_CHANGE/*what*/, config /*obj*/);
+ mRecordingCallbackHandler.sendMessage(m);
+ }
+ }
+ }
+ }
+ };
+
+ @GuardedBy("mRecordCallbackLock")
+ private void beginRecordingCallbackHandling() {
+ if (mRecordingCallbackHandlerThread == null) {
+ mRecordingCallbackHandlerThread = new HandlerThread(TAG + ".RecordingCallback");
+ mRecordingCallbackHandlerThread.start();
+ final Looper looper = mRecordingCallbackHandlerThread.getLooper();
+ if (looper != null) {
+ mRecordingCallbackHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_RECORDING_CONFIG_CHANGE: {
+ if (msg.obj == null) {
+ return;
+ }
+ ArrayList<AudioRecordingConfiguration> configs =
+ new ArrayList<AudioRecordingConfiguration>();
+ configs.add((AudioRecordingConfiguration) msg.obj);
+
+ final LinkedList<AudioRecordingCallbackInfo> cbInfoList;
+ synchronized (mRecordCallbackLock) {
+ if (mRecordCallbackList.size() == 0) {
+ return;
+ }
+ cbInfoList = new LinkedList<AudioRecordingCallbackInfo>(
+ mRecordCallbackList);
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (AudioRecordingCallbackInfo cbi : cbInfoList) {
+ cbi.mExecutor.execute(() ->
+ cbi.mCb.onRecordingConfigChanged(configs));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } break;
+ default:
+ Log.e(TAG, "Unknown event " + msg.what);
+ break;
+ }
+ }
+ };
+ final IAudioService service = getService();
+ try {
+ service.registerRecordingCallback(mRecordingCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mRecordCallbackLock")
+ private void endRecordingCallbackHandling() {
+ if (mRecordingCallbackHandlerThread != null) {
+ final IAudioService service = getService();
+ try {
+ service.unregisterRecordingCallback(mRecordingCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mRecordingCallbackHandlerThread.quit();
+ mRecordingCallbackHandlerThread = null;
+ }
+ }
+
+ AudioRecordingConfiguration getMyConfig(List<AudioRecordingConfiguration> configs) {
+ int portId = mClient.getPortId();
+ for (AudioRecordingConfiguration config : configs) {
+ if (config.getClientPortId() == portId) {
+ return config;
+ }
+ }
+ return null;
+ }
+
+ private static IAudioService getService() {
+ if (sService != null) {
+ return sService;
+ }
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ sService = IAudioService.Stub.asInterface(b);
+ return sService;
+ }
+}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 36f635a..58fc1ab 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -20,6 +20,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.os.Build;
import android.util.Log;
@@ -334,7 +335,9 @@
* @param packName package name of the client app performing the recording. NOT SUPPORTED
*/
void onRecordingConfigurationChanged(int event, int uid, int session, int source,
- int[] recordingFormat, String packName);
+ int portId, boolean silenced, int[] recordingFormat,
+ AudioEffect.Descriptor[] clienteffects, AudioEffect.Descriptor[] effects,
+ int activeSource, String packName);
}
private static AudioRecordingCallback sRecordingCallback;
@@ -352,19 +355,27 @@
* @param session
* @param source
* @param recordingFormat see
- * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int[])}
+ * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int,\
+ boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
* for the description of the record format.
*/
@UnsupportedAppUsage
private static void recordingCallbackFromNative(int event, int uid, int session, int source,
- int[] recordingFormat) {
+ int portId, boolean silenced, int[] recordingFormat,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects,
+ int activeSource) {
AudioRecordingCallback cb = null;
synchronized (AudioSystem.class) {
cb = sRecordingCallback;
}
+
+ String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
+ String effectName = effects.length == 0 ? "None" : effects[0].name;
+
if (cb != null) {
// TODO receive package name from native
- cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
+ cb.onRecordingConfigurationChanged(event, uid, session, source, portId, silenced,
+ recordingFormat, clientEffects, effects, activeSource, "");
}
}
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index aca8dbe..e29bd00 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -44,6 +44,8 @@
private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
+ private int mCount = 0;
+ private boolean mClosed = false;
private FileDataSourceDesc() {
super();
@@ -55,23 +57,48 @@
@Override
void close() {
super.close();
- closeFD();
+ decCount();
}
/**
- * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+ * Decrements usage count by {@link MediaPlayer2}.
+ * If this is the last usage, also releases the file descriptor held by this
+ * {@code FileDataSourceDesc} object.
*/
- void closeFD() {
+ void decCount() {
synchronized (this) {
- if (mPFD != null) {
- try {
- mPFD.close();
- } catch (IOException e) {
- Log.e(TAG, "failed to close pfd: " + e);
- }
-
- mPFD = null;
+ --mCount;
+ if (mCount > 0) {
+ return;
}
+
+ try {
+ mPFD.close();
+ mClosed = true;
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close pfd: " + e);
+ }
+ }
+ }
+
+ /**
+ * Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
+ */
+ void incCount() {
+ synchronized (this) {
+ if (!mClosed) {
+ ++mCount;
+ }
+ }
+ }
+
+ /**
+ * Return the status of underline ParcelFileDescriptor
+ * @return true if underline ParcelFileDescriptor is closed, false otherwise.
+ */
+ boolean isPFDClosed() {
+ synchronized (this) {
+ return mClosed;
}
}
@@ -150,6 +177,16 @@
* @return a new {@link FileDataSourceDesc} object
*/
public @NonNull FileDataSourceDesc build() {
+ if (mPFD == null) {
+ throw new IllegalStateException(
+ "underline ParcelFileDescriptor should not be null");
+ }
+ try {
+ mPFD.getFd();
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException("ParcelFileDescriptor has been closed");
+ }
+
FileDataSourceDesc dsd = new FileDataSourceDesc();
super.build(dsd);
dsd.mPFD = mPFD;
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
new file mode 100644
index 0000000..aa2a937
--- /dev/null
+++ b/media/java/android/media/MediaItem2.java
@@ -0,0 +1,324 @@
+/*
+ * 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 static android.media.MediaMetadata.METADATA_KEY_MEDIA_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A class with information on a single media item with the metadata information. Here are use
+ * cases.
+ * <ul>
+ * <li>Specify media items to {@link SessionPlayer2} for playback.
+ * <li>Share media items across the processes.
+ * </ul>
+ * <p>
+ * Subclasses of the session player may only accept certain subclasses of the media items. Check
+ * the player documentation that you're interested in.
+ * <p>
+ * When it's shared across the processes, we cannot guarantee that they contain the right values
+ * because media items are application dependent especially for the metadata.
+ * <p>
+ * This object is thread safe.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link androidx.media2.MediaItem} for consistent behavior across all devices.
+ * </p>
+ * @hide
+ */
+public class MediaItem2 implements Parcelable {
+ private static final String TAG = "MediaItem2";
+
+ // intentionally less than long.MAX_VALUE.
+ // Declare this first to avoid 'illegal forward reference'.
+ static final long LONG_MAX = 0x7ffffffffffffffL;
+
+ /**
+ * Used when a position is unknown.
+ *
+ * @see #getEndPosition()
+ */
+ public static final long POSITION_UNKNOWN = LONG_MAX;
+
+ public static final Parcelable.Creator<MediaItem2> CREATOR =
+ new Parcelable.Creator<MediaItem2>() {
+ @Override
+ public MediaItem2 createFromParcel(Parcel in) {
+ return new MediaItem2(in);
+ }
+
+ @Override
+ public MediaItem2[] newArray(int size) {
+ return new MediaItem2[size];
+ }
+ };
+
+ // TODO: Use SessionPlayer2.UNKNOWN_TIME instead
+ private static final long UNKNOWN_TIME = -1;
+
+ private final long mStartPositionMs;
+ private final long mEndPositionMs;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private MediaMetadata mMetadata;
+ @GuardedBy("mLock")
+ private final List<Pair<OnMetadataChangedListener, Executor>> mListeners = new ArrayList<>();
+
+ /**
+ * Used by {@link MediaItem2.Builder}.
+ */
+ // Note: Needs to be protected when we want to allow 3rd party player to define customized
+ // MediaItem2.
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(Builder builder) {
+ this(builder.mMetadata, builder.mStartPositionMs, builder.mEndPositionMs);
+ }
+
+ /**
+ * Used by Parcelable.Creator.
+ */
+ // Note: Needs to be protected when we want to allow 3rd party player to define customized
+ // MediaItem2.
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(Parcel in) {
+ this(in.readParcelable(MediaItem2.class.getClassLoader()), in.readLong(), in.readLong());
+ }
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(MediaItem2 item) {
+ this(item.mMetadata, item.mStartPositionMs, item.mEndPositionMs);
+ }
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaItem2(@Nullable MediaMetadata metadata, long startPositionMs, long endPositionMs) {
+ if (startPositionMs > endPositionMs) {
+ throw new IllegalArgumentException("Illegal start/end position: "
+ + startPositionMs + " : " + endPositionMs);
+ }
+ if (metadata != null && metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+ long durationMs = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+ if (durationMs != UNKNOWN_TIME && endPositionMs != POSITION_UNKNOWN
+ && endPositionMs > durationMs) {
+ throw new IllegalArgumentException("endPositionMs shouldn't be greater than"
+ + " duration in the metdata, endPositionMs=" + endPositionMs
+ + ", durationMs=" + durationMs);
+ }
+ }
+ mMetadata = metadata;
+ mStartPositionMs = startPositionMs;
+ mEndPositionMs = endPositionMs;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
+ synchronized (mLock) {
+ sb.append("{mMetadata=").append(mMetadata);
+ sb.append(", mStartPositionMs=").append(mStartPositionMs);
+ sb.append(", mEndPositionMs=").append(mEndPositionMs);
+ sb.append('}');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Sets metadata. If the metadata is not {@code null}, its id should be matched with this
+ * instance's media id.
+ *
+ * @param metadata metadata to update
+ * @see MediaMetadata#METADATA_KEY_MEDIA_ID
+ */
+ public void setMetadata(@Nullable MediaMetadata metadata) {
+ List<Pair<OnMetadataChangedListener, Executor>> listeners = new ArrayList<>();
+ synchronized (mLock) {
+ if (mMetadata != null && metadata != null
+ && !TextUtils.equals(getMediaId(), metadata.getString(METADATA_KEY_MEDIA_ID))) {
+ Log.d(TAG, "MediaItem2's media ID shouldn't be changed");
+ return;
+ }
+ mMetadata = metadata;
+ listeners.addAll(mListeners);
+ }
+
+ for (Pair<OnMetadataChangedListener, Executor> pair : listeners) {
+ final OnMetadataChangedListener listener = pair.first;
+ pair.second.execute(new Runnable() {
+ @Override
+ public void run() {
+ listener.onMetadataChanged(MediaItem2.this);
+ }
+ });
+ }
+ }
+
+ /**
+ * Gets the metadata of the media.
+ *
+ * @return metadata from the session
+ */
+ public @Nullable MediaMetadata getMetadata() {
+ synchronized (mLock) {
+ return mMetadata;
+ }
+ }
+
+ /**
+ * Return the position in milliseconds at which the playback will start.
+ * @return the position in milliseconds at which the playback will start
+ */
+ public long getStartPosition() {
+ return mStartPositionMs;
+ }
+
+ /**
+ * Return the position in milliseconds at which the playback will end.
+ * {@link #POSITION_UNKNOWN} means ending at the end of source content.
+ * @return the position in milliseconds at which the playback will end
+ */
+ public long getEndPosition() {
+ return mEndPositionMs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mMetadata, 0);
+ dest.writeLong(mStartPositionMs);
+ dest.writeLong(mEndPositionMs);
+ }
+
+ /**
+ * Gets the media id for this item. If it's not {@code null}, it's a persistent unique key
+ * for the underlying media content.
+ *
+ * @return media Id from the session
+ */
+ @Nullable String getMediaId() {
+ synchronized (mLock) {
+ return mMetadata != null
+ ? mMetadata.getString(METADATA_KEY_MEDIA_ID) : null;
+ }
+ }
+
+ void addOnMetadataChangedListener(Executor executor, OnMetadataChangedListener listener) {
+ synchronized (mLock) {
+ for (Pair<OnMetadataChangedListener, Executor> pair : mListeners) {
+ if (pair.first == listener) {
+ return;
+ }
+ }
+ mListeners.add(new Pair<>(listener, executor));
+ }
+ }
+
+ void removeOnMetadataChangedListener(OnMetadataChangedListener listener) {
+ synchronized (mLock) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ if (mListeners.get(i).first == listener) {
+ mListeners.remove(i);
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Builder for {@link MediaItem2}.
+ */
+ public static class Builder {
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaMetadata mMetadata;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ long mStartPositionMs = 0;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ long mEndPositionMs = POSITION_UNKNOWN;
+
+ /**
+ * Set the metadata of this instance. {@code null} for unset.
+ *
+ * @param metadata metadata
+ * @return this instance for chaining
+ */
+ public @NonNull Builder setMetadata(@Nullable MediaMetadata metadata) {
+ mMetadata = metadata;
+ return this;
+ }
+
+ /**
+ * Sets the start position in milliseconds at which the playback will start.
+ * Any negative number is treated as 0.
+ *
+ * @param position the start position in milliseconds at which the playback will start
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setStartPosition(long position) {
+ if (position < 0) {
+ position = 0;
+ }
+ mStartPositionMs = position;
+ return this;
+ }
+
+ /**
+ * Sets the end position in milliseconds at which the playback will end.
+ * Any negative number is treated as maximum length of the media item.
+ *
+ * @param position the end position in milliseconds at which the playback will end
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setEndPosition(long position) {
+ if (position < 0) {
+ position = POSITION_UNKNOWN;
+ }
+ mEndPositionMs = position;
+ return this;
+ }
+
+ /**
+ * Build {@link MediaItem2}.
+ *
+ * @return a new {@link MediaItem2}.
+ */
+ public @NonNull MediaItem2 build() {
+ return new MediaItem2(this);
+ }
+ }
+
+ interface OnMetadataChangedListener {
+ void onMetadataChanged(MediaItem2 item);
+ }
+}
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b137ce2..cef6614 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -436,13 +436,6 @@
*/
// This is a synchronous call.
public void reset() {
- synchronized (mEventCbLock) {
- mEventCallbackRecords.clear();
- }
- synchronized (mDrmEventCbLock) {
- mDrmEventCallbackRecords.clear();
- }
-
clearSourceInfos();
stayAwake(false);
@@ -696,7 +689,7 @@
return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
int state = getState();
try {
if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
@@ -729,7 +722,7 @@
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ checkDataSourceDesc(dsd);
synchronized (mSrcLock) {
clearNextSourceInfos_l();
mNextSourceInfos.add(new SourceInfo(dsd));
@@ -755,15 +748,35 @@
if (dsds == null || dsds.size() == 0) {
throw new IllegalArgumentException("data source list cannot be null or empty.");
}
+ boolean hasError = false;
+ for (DataSourceDesc dsd : dsds) {
+ if (dsd != null) {
+ hasError = true;
+ continue;
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ hasError = true;
+ continue;
+ }
+
+ fdsd.incCount();
+ }
+ }
+ if (hasError) {
+ for (DataSourceDesc dsd : dsds) {
+ if (dsd != null) {
+ dsd.close();
+ }
+ }
+ throw new IllegalArgumentException("invalid data source list");
+ }
synchronized (mSrcLock) {
clearNextSourceInfos_l();
for (DataSourceDesc dsd : dsds) {
- if (dsd != null) {
- mNextSourceInfos.add(new SourceInfo(dsd));
- } else {
- Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
- }
+ mNextSourceInfos.add(new SourceInfo(dsd));
}
}
prepareNextDataSource();
@@ -771,6 +784,20 @@
});
}
+ // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed.
+ private void checkDataSourceDesc(DataSourceDesc dsd) {
+ if (dsd == null) {
+ throw new IllegalArgumentException("dsd is expected to be non null");
+ }
+ if (dsd instanceof FileDataSourceDesc) {
+ FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+ if (fdsd.isPFDClosed()) {
+ throw new IllegalArgumentException("the underline FileDescriptor has been closed");
+ }
+ fdsd.incCount();
+ }
+ }
+
/**
* Removes all data sources pending to be played.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -2724,6 +2751,12 @@
"Illegal null Executor for the EventCallback");
}
synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ if (cb.first == executor && cb.second == eventCallback) {
+ Log.w(TAG, "The callback has been registered before.");
+ return;
+ }
+ }
mEventCallbackRecords.add(new Pair(executor, eventCallback));
}
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 8ced021..1cdc291 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,7 +16,9 @@
package android.media;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
@@ -40,6 +42,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
+
/**
* Used to record audio and video. The recording control is based on a
@@ -83,7 +87,9 @@
* <a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a> developer guide.</p>
* </div>
*/
-public class MediaRecorder implements AudioRouting
+public class MediaRecorder implements AudioRouting,
+ AudioRecordingMonitor,
+ AudioRecordingMonitorClient
{
static {
System.loadLibrary("media_jni");
@@ -304,7 +310,7 @@
/**
* Audio source for preemptible, low-priority software hotword detection
- * It presents the same gain and pre processing tuning as {@link #VOICE_RECOGNITION}.
+ * It presents the same gain and pre-processing tuning as {@link #VOICE_RECOGNITION}.
* <p>
* An application should use this audio source when it wishes to do
* always-on software hotword detection, while gracefully giving in to any other application
@@ -1471,6 +1477,57 @@
private native final int native_getActiveMicrophones(
ArrayList<MicrophoneInfo> activeMicrophones);
+ //--------------------------------------------------------------------------
+ // Implementation of AudioRecordingMonitor interface
+ //--------------------
+
+ AudioRecordingMonitorImpl mRecordingInfoImpl =
+ new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this);
+
+ /**
+ * Register a callback to be notified of audio capture changes via a
+ * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
+ * configuration changes (pre-processing, format, sampling rate...) or capture is
+ * silenced/unsilenced by the system.
+ * @param executor {@link Executor} to handle the callbacks.
+ * @param cb non-null callback to register
+ */
+ public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AudioManager.AudioRecordingCallback cb) {
+ mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb);
+ }
+
+ /**
+ * Unregister an audio recording callback previously registered with
+ * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
+ * @param cb non-null callback to unregister
+ */
+ public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
+ mRecordingInfoImpl.unregisterAudioRecordingCallback(cb);
+ }
+
+ /**
+ * Returns the current active audio recording for this audio recorder.
+ * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
+ * or null otherwise.
+ * @see AudioRecordingConfiguration
+ */
+ public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
+ return mRecordingInfoImpl.getActiveRecordingConfiguration();
+ }
+
+ //---------------------------------------------------------
+ // Implementation of AudioRecordingMonitorClient interface
+ //--------------------
+ /**
+ * @hide
+ */
+ public int getPortId() {
+ return native_getPortId();
+ }
+
+ private native int native_getPortId();
+
/**
* Called from native code when an interesting event happens. This method
* just uses the EventHandler system to post the event back to the main app thread.
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 84dfcb1..42597aa 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -60,6 +61,8 @@
private final Context mContext;
private final AudioManager mAudioManager;
+ private VolumeShaper.Configuration mVolumeShaperConfig;
+ private VolumeShaper mVolumeShaper;
/**
* Flag indicating if we're allowed to fall back to remote playback using
@@ -302,6 +305,18 @@
*/
@UnsupportedAppUsage
public void setUri(Uri uri) {
+ setUri(uri, null);
+ }
+
+ /**
+ * Set {@link Uri} to be used for ringtone playback. Attempts to open
+ * locally, otherwise will delegate playback to remote
+ * {@link IRingtonePlayer}. Add {@link VolumeShaper} if required.
+ *
+ * @hide
+ */
+ public void setUri(Uri uri, @Nullable VolumeShaper.Configuration volumeShaperConfig) {
+ mVolumeShaperConfig = volumeShaperConfig;
destroyLocalPlayer();
mUri = uri;
@@ -319,6 +334,9 @@
synchronized (mPlaybackSettingsLock) {
applyPlaybackProperties_sync();
}
+ if (mVolumeShaperConfig != null) {
+ mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
+ }
mLocalPlayer.prepare();
} catch (SecurityException | IOException e) {
@@ -412,6 +430,9 @@
}
mLocalPlayer.setOnCompletionListener(mCompletionListener);
mLocalPlayer.start();
+ if (mVolumeShaper != null) {
+ mVolumeShaper.apply(VolumeShaper.Operation.PLAY);
+ }
}
/**
@@ -458,6 +479,9 @@
synchronized (mPlaybackSettingsLock) {
applyPlaybackProperties_sync();
}
+ if (mVolumeShaperConfig != null) {
+ mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
+ }
mLocalPlayer.prepare();
startLocalPlayer();
afd.close();
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 874f21e..0679e8e9 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -694,6 +694,27 @@
return getRingtone(context, ringtoneUri, -1);
}
+ /**
+ * Returns a {@link Ringtone} with {@link VolumeShaper} if required for a given sound URI.
+ * <p>
+ * If the given URI cannot be opened for any reason, this method will
+ * attempt to fallback on another sound. If it cannot find any, it will
+ * return null.
+ *
+ * @param context A context used to query.
+ * @param ringtoneUri The {@link Uri} of a sound or ringtone.
+ * @param volumeShaperConfig config for volume shaper of the ringtone if applied.
+ * @return A {@link Ringtone} for the given URI, or null.
+ *
+ * @hide
+ */
+ public static Ringtone getRingtone(
+ final Context context, Uri ringtoneUri,
+ @Nullable VolumeShaper.Configuration volumeShaperConfig) {
+ // Don't set the stream type
+ return getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig);
+ }
+
//FIXME bypass the notion of stream types within the class
/**
* Returns a {@link Ringtone} for a given sound URI on the given stream
@@ -707,13 +728,32 @@
*/
@UnsupportedAppUsage
private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
+ return getRingtone(context, ringtoneUri, streamType, null /* volumeShaperConfig */);
+ }
+
+ //FIXME bypass the notion of stream types within the class
+ /**
+ * Returns a {@link Ringtone} with {@link VolumeShaper} if required for a given sound URI on
+ * the given stream type. Normally, if you change the stream type on the returned
+ * {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
+ * an optimized route to avoid that.
+ *
+ * @param streamType The stream type for the ringtone, or -1 if it should
+ * not be set (and the default used instead).
+ * @param volumeShaperConfig config for volume shaper of the ringtone if applied.
+ * @see #getRingtone(Context, Uri)
+ */
+ @UnsupportedAppUsage
+ private static Ringtone getRingtone(
+ final Context context, Uri ringtoneUri, int streamType,
+ @Nullable VolumeShaper.Configuration volumeShaperConfig) {
try {
final Ringtone r = new Ringtone(context, true);
if (streamType >= 0) {
//FIXME deprecated call
r.setStreamType(streamType);
}
- r.setUri(ringtoneUri);
+ r.setUri(ringtoneUri, volumeShaperConfig);
return r;
} catch (Exception ex) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index b3a8b21..ca30f32 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -763,6 +763,20 @@
}
return jStatus;
}
+
+static jint android_media_MediaRecord_getPortId(JNIEnv *env, jobject thiz) {
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ if (mr == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return (jint)AUDIO_PORT_HANDLE_NONE;
+ }
+
+ audio_port_handle_t portId;
+ process_media_recorder_call(env, mr->getPortId(&portId),
+ "java/lang/RuntimeException", "getPortId failed.");
+ return (jint)portId;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -801,6 +815,7 @@
{"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback},
{"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones},
+ {"native_getPortId", "()I", (void *)android_media_MediaRecord_getPortId},
};
// This function only registers the native methods, and is called from
diff --git a/native/android/net.c b/native/android/net.c
index e32b787..4cac371 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -84,8 +84,7 @@
return android_getaddrinfofornet(node, service, hints, netid, 0, res);
}
-int android_res_nquery(net_handle_t network,
- const char *dname, int ns_class, int ns_type) {
+int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
@@ -94,12 +93,11 @@
return resNetworkQuery(netid, dname, ns_class, ns_type);
}
-int android_res_nresult(int fd, int *rcode, unsigned char *answer, int anslen) {
+int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) {
return resNetworkResult(fd, rcode, answer, anslen);
}
-int android_res_nsend(net_handle_t network,
- const unsigned char *msg, int msglen) {
+int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 74d6605..9b6ad38 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -69,6 +69,7 @@
],
},
resource_dirs: [
+ "res-keyguard",
"res",
],
@@ -80,4 +81,5 @@
"com.android.keyguard",
],
+ annotation_processors: ["dagger2-compiler-2.19"],
}
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
new file mode 100644
index 0000000..f3a2f0f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
new file mode 100644
index 0000000..ef0aac2
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16.2l-3.5,-3.5a0.984,0.984 0,0 0,-1.4 0,0.984 0.984,0 0,0 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7a0.984,0.984 0,0 0,0 -1.4,0.984 0.984,0 0,0 -1.4,0L9,16.2z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
new file mode 100644
index 0000000..b428931
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/car_button_radius"/>
+ <solid android:color="#131315"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/car_button_radius"/>
+ <solid android:color="@color/button_background"/>
+ </shape>
+ </item>
+</selector>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
new file mode 100644
index 0000000..b115a1f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<!-- Car customizations
+ - Added title "Enter your Pattern" at the top
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_pattern_view"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="@dimen/car_margin">
+
+ <FrameLayout
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1">
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="@dimen/keyguard_pattern_dimension"
+ android:layout_height="@dimen/keyguard_pattern_dimension"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pattern" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal"
+ android:visibility="gone" />
+ </LinearLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
new file mode 100644
index 0000000..ed88c62
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<!-- Car customizations
+ - Added title "Enter your PIN" under the entry field
+ - Put backspace and enter buttons in row 4
+ - PIN pad is on start side while entry field and title are on the end side
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:paddingHorizontal="@dimen/car_margin">
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent">
+
+ <GridLayout
+ android:id="@+id/container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:columnCount="3">
+
+ <include layout="@layout/num_pad_keys"/>
+ </GridLayout>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <com.android.keyguard.PasswordTextView
+ android:id="@+id/pinEntry"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/pin_entry_height"
+ android:gravity="center"
+ app:scaledTextSize="@integer/password_text_view_scale"
+ android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/divider_height"
+ android:background="@android:color/white" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pin" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+ </LinearLayout>
+
+ <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+ keyguard to crash. Instead put them down here where they are out of the way and set their
+ visibility to gone. -->
+ <com.android.keyguard.AlphaOptimizedRelativeLayout
+ android:id="@+id/row0"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row3"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row4"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
new file mode 100644
index 0000000..062f7bd
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -0,0 +1,32 @@
+<?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
+ -->
+
+<!-- Car customizations
+ Car has solid black background instead of a transparent one
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:fitsSystemWindows="true">
+
+ <include
+ style="@style/BouncerSecurityContainer"
+ layout="@layout/keyguard_host_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</FrameLayout>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
new file mode 100644
index 0000000..c230414
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<com.android.keyguard.KeyguardMessageArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ style="@style/Keyguard.TextView"
+ android:id="@+id/keyguard_message_area"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:layout_marginBottom="@dimen/car_padding_4"
+ android:textSize="@dimen/car_body2_size" />
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
new file mode 100644
index 0000000..c7eda38
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
@@ -0,0 +1,37 @@
+<?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
+ -->
+
+<!-- Car customizations
+ The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+ Removing it will cause the keyguard to crash. Hide them instead
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <TextView
+ android:id="@+id/digit_text"
+ style="@style/Widget.TextView.NumPadKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <!-- The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+ Removing it will cause the keyguard to crash. Hide them instead -->
+ <TextView
+ android:id="@+id/klondike_text"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ />
+</merge>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
new file mode 100644
index 0000000..e701fdb
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<!-- Car customizations
+ - Added title "Enter your Password" below the password field
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_password_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+ android:gravity="center">
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <!-- Password entry field -->
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:orientation="vertical"
+ android:theme="?attr/passwordStyle">
+
+ <EditText
+ android:id="@+id/passwordEntry"
+ android:layout_width="@dimen/password_field_width"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:singleLine="true"
+ android:textStyle="normal"
+ android:inputType="textPassword"
+ android:textSize="@dimen/car_body1_size"
+ android:textColor="?attr/wallpaperTextColor"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:imeOptions="flagForceAscii|actionDone"
+ android:maxLength="@integer/password_text_view_scale"
+ />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_password" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ <ImageView android:id="@+id/switch_ime_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:src="@drawable/ic_lockscreen_ime"
+ android:contentDescription="@string/accessibility_ime_switch_button"
+ android:clickable="true"
+ android:padding="8dp"
+ android:tint="@color/background_protected"
+ android:layout_gravity="end|center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal"
+ android:visibility="gone"
+ />
+
+</com.android.keyguard.KeyguardPasswordView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
new file mode 100644
index 0000000..00333a8
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<!-- Car customizations
+ - Added title "Enter your Pattern" at the top
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pattern_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+ android:gravity="center_horizontal">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pattern" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="@dimen/keyguard_pattern_dimension"
+ android:layout_height="@dimen/keyguard_pattern_dimension"
+ android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center" />
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal"
+ android:visibility="gone" />
+ </LinearLayout>
+ </FrameLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..1662251
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<!-- Car customizations
+ - Added title "Enter your PIN" under the entry field
+ - Put backspace and enter buttons in row 4
+ - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="@dimen/num_pad_margin_left"
+ android:layout_marginRight="@dimen/num_pad_margin_right"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <com.android.keyguard.PasswordTextView
+ android:id="@+id/pinEntry"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/pin_entry_height"
+ android:gravity="center"
+ app:scaledTextSize="@integer/password_text_view_scale"
+ android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/divider_height"
+ android:background="@android:color/white" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/car_padding_2"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/car_body1_size"
+ android:text="@string/keyguard_enter_your_pin" />
+
+ <include layout="@layout/keyguard_message_area" />
+
+ </LinearLayout>
+
+ <GridLayout
+ android:id="@+id/container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+ android:columnCount="3">
+
+ <include layout="@layout/num_pad_keys"/>
+ </GridLayout>
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ style="@style/KeyguardButton"
+ android:text="@string/cancel"/>
+
+ </LinearLayout>
+
+ <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+ keyguard to crash. Instead put them down here where they are out of the way and set their
+ visibility to gone. -->
+ <com.android.keyguard.AlphaOptimizedRelativeLayout
+ android:id="@+id/row0"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row3"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row4"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+
+ <include
+ layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
new file mode 100644
index 0000000..8306cb4
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
@@ -0,0 +1,83 @@
+<?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
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <!-- Row 1 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key1"
+ style="@style/NumPadKeyButton"
+ app:digit="1" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key2"
+ style="@style/NumPadKeyButton.MiddleColumn"
+ app:digit="2" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key3"
+ style="@style/NumPadKeyButton"
+ app:digit="3" />
+
+ <!-- Row 2 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key4"
+ style="@style/NumPadKeyButton"
+ app:digit="4" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key5"
+ style="@style/NumPadKeyButton.MiddleColumn"
+ app:digit="5" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key6"
+ style="@style/NumPadKeyButton"
+ app:digit="6" />
+
+ <!-- Row 3 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key7"
+ style="@style/NumPadKeyButton"
+ app:digit="7" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key8"
+ style="@style/NumPadKeyButton.MiddleColumn"
+ app:digit="8" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key9"
+ style="@style/NumPadKeyButton"
+ app:digit="9" />
+
+ <!-- Row 4 -->
+ <ImageButton
+ android:id="@+id/delete_button"
+ style="@style/NumPadKeyButton.LastRow"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_backspace"
+ android:clickable="true"
+ android:tint="@android:color/white"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/keyboardview_keycode_delete" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key0"
+ style="@style/NumPadKeyButton.LastRow.MiddleColumn"
+ app:digit="0" />
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@style/NumPadKeyButton.LastRow"
+ android:src="@drawable/ic_done"
+ android:tint="@android:color/white"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/keyboardview_keycode_enter" />
+</merge>
+
diff --git a/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
new file mode 100644
index 0000000..d055efa
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<resources>
+ <dimen name="pin_pattern_pad_margin_vertical">178dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values-land/dimens.xml b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
new file mode 100644
index 0000000..805a134
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<resources>
+ <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_5</dimen>
+ <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_4</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/colors.xml b/packages/CarSystemUI/res-keyguard/values/colors.xml
new file mode 100644
index 0000000..e6edbea3
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<resources>
+ <color name="button_background">@color/car_dark_blue_grey_600</color>
+ <color name="button_text">@color/car_action1_light</color>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res-keyguard/values/dimens.xml b/packages/CarSystemUI/res-keyguard/values/dimens.xml
new file mode 100644
index 0000000..9424dc3
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<resources>
+ <dimen name="num_pad_margin_left">112dp</dimen>
+ <dimen name="num_pad_margin_right">144dp</dimen>
+ <dimen name="num_pad_key_width">80dp</dimen>
+ <dimen name="num_pad_key_height">80dp</dimen>
+ <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_6</dimen>
+ <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_5</dimen>
+ <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
+ <dimen name="divider_height">1dp</dimen>
+ <dimen name="key_enter_margin_top">128dp</dimen>
+ <dimen name="keyguard_pattern_dimension">400dp</dimen>
+ <dimen name="password_field_width">350dp</dimen>
+ <dimen name="pin_pattern_pad_margin_vertical">0dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/integers.xml b/packages/CarSystemUI/res-keyguard/values/integers.xml
new file mode 100644
index 0000000..bad1346
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<resources>
+ <integer name="password_text_view_scale">40</integer>
+ <integer name="password_max_length">500</integer>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/styles.xml b/packages/CarSystemUI/res-keyguard/values/styles.xml
new file mode 100644
index 0000000..b39e6e6
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/styles.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- The style for the volume icons in the volume dialog. This style makes the icon scale to
+ fit its container since auto wants the icon to be larger. The padding is added to make it
+ so the icon does not press along the edges of the dialog. -->
+ <style name="NumPadKeyButton">
+ <item name="android:layout_width">@dimen/num_pad_key_width</item>
+ <item name="android:layout_height">@dimen/num_pad_key_height</item>
+ <item name="android:layout_marginBottom">@dimen/num_pad_key_margin_bottom</item>
+ <item name="textView">@id/pinEntry</item>
+ </style>
+
+ <style name="NumPadKeyButton.MiddleColumn">
+ <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+ <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+ </style>
+
+ <style name="NumPadKeyButton.LastRow">
+ <item name="android:layout_marginBottom">0dp</item>
+ </style>
+
+ <style name="NumPadKeyButton.LastRow.MiddleColumn">
+ <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+ <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+ </style>
+
+ <style name="KeyguardButton" parent="Widget.Car.Button">
+ <item name="android:background">@drawable/keyguard_button_background</item>
+ <item name="android:textColor">@color/button_text</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
+ <!-- Only replaces the text size. -->
+ <item name="android:textSize">@dimen/car_body1_size</item>
+ </style>
+</resources>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index c527711..2e05c38 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -32,7 +32,7 @@
SystemUi b/c it can't be overlayed at this level for now
-->
<string-array name="config_systemUIServiceComponents" translatable="false">
- <item>com.android.systemui.Dependency</item>
+ <item>com.android.systemui.Dependency$DependencyCreator</item>
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index f57f26d..3c0a297 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -17,25 +17,44 @@
package com.android.systemui;
import android.content.Context;
-import android.util.ArrayMap;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.car.CarNotificationEntryManager;
+import com.android.systemui.car.CarNotificationInterruptionStateProvider;
import com.android.systemui.statusbar.car.CarFacetButtonController;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.volume.CarVolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogComponent;
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
/**
* Class factory to provide car specific SystemUI components.
*/
public class CarSystemUIFactory extends SystemUIFactory {
+ private CarDependencyComponent mCarDependencyComponent;
+
+ @Override
+ protected void init(Context context) {
+ super.init(context);
+ mCarDependencyComponent = DaggerCarSystemUIFactory_CarDependencyComponent.builder()
+ .contextHolder(new ContextHolder(context))
+ .build();
+ }
+
+ public CarDependencyComponent getCarDependencyComponent() {
+ return mCarDependencyComponent;
+ }
+
public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -46,12 +65,33 @@
}
@Override
- public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
- Context context) {
- super.injectDependencies(providers, context);
- providers.put(NotificationEntryManager.class,
- () -> new CarNotificationEntryManager(context));
- providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
- providers.put(HvacController.class, () -> new HvacController(context));
+ public NotificationEntryManager provideNotificationEntryManager(Context context) {
+ return new CarNotificationEntryManager(context);
+ }
+
+ @Override
+ public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+ Context context) {
+ return new CarNotificationInterruptionStateProvider(context);
+ }
+
+ @Module
+ protected static class ContextHolder {
+ private Context mContext;
+
+ public ContextHolder(Context context) {
+ mContext = context;
+ }
+
+ @Provides
+ public Context provideContext() {
+ return mContext;
+ }
+ }
+
+ @Singleton
+ @Component(modules = ContextHolder.class)
+ public interface CarDependencyComponent {
+ CarFacetButtonController getCarFacetButtonController();
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 0563418..323cae0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,7 +18,6 @@
import android.content.Context;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -39,16 +38,4 @@
// long click listener.
return null;
}
-
- @Override
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by not pinning any
- // notification if the shade is already opened.
- if (!getPresenter().isPresenterFullyCollapsed()) {
- return false;
- }
-
- return super.shouldHeadsUp(entry);
- }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..62502ef
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -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.
+ */
+
+package com.android.systemui.car;
+
+import android.content.Context;
+
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+
+/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
+public class CarNotificationInterruptionStateProvider extends
+ NotificationInterruptionStateProvider {
+ public CarNotificationInterruptionStateProvider(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ // Because space is usually constrained in the auto use-case, there should not be a
+ // pinned notification when the shade has been expanded. Ensure this by not pinning any
+ // notification if the shade is already opened.
+ if (!getPresenter().isPresenterFullyCollapsed()) {
+ return false;
+ }
+
+ return super.shouldHeadsUp(entry);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index cea4ab0..0a20eaa 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -26,8 +26,9 @@
import android.widget.LinearLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.Dependency;
+import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
/**
* CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
@@ -76,8 +77,9 @@
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
setupIntents(typedArray);
setupIcons(typedArray);
- CarFacetButtonController carFacetButtonController = Dependency.get(
- CarFacetButtonController.class);
+ CarSystemUIFactory factory = SystemUIFactory.getInstance();
+ CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
+ .getCarFacetButtonController();
carFacetButtonController.addFacetButton(this);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 56db242..7811a1c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -29,11 +29,15 @@
import java.util.List;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* CarFacetButtons placed on the nav bar are designed to have visual indication that the active
* application on screen is associated with it. This is basically a similar concept to a radio
* button group.
*/
+@Singleton
public class CarFacetButtonController {
protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
@@ -42,6 +46,7 @@
protected CarFacetButton mSelectedFacetButton;
protected Context mContext;
+ @Inject
public CarFacetButtonController(Context context) {
mContext = context;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 5da236c..7028999c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -29,9 +29,11 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BatteryMeterView;
+import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
@@ -102,7 +104,9 @@
mHvacController.connectToCarService();
- mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
+ CarSystemUIFactory factory = SystemUIFactory.getInstance();
+ mCarFacetButtonController = factory.getCarDependencyComponent()
+ .getCarFacetButtonController();
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
if (!mDeviceIsProvisioned) {
@@ -239,7 +243,7 @@
@Override
protected void makeStatusBarView() {
super.makeStatusBarView();
- mHvacController = Dependency.get(HvacController.class);
+ mHvacController = new HvacController(mContext);
mNotificationPanelBackground = getDefaultWallpaper();
mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index aec31ee..30429ed 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.car.hvac;
+import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+
import android.car.Car;
+import android.car.VehicleUnit;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
@@ -167,7 +171,17 @@
private void initComponent(TemperatureView view) {
int id = view.getPropertyId();
int zone = view.getAreaId();
+
try {
+ if (mHvacManager != null
+ && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+ VEHICLE_AREA_TYPE_GLOBAL)) {
+ if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+ VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) {
+ view.setDisplayInFahrenheit(true);
+ }
+
+ }
if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
view.setTemp(Float.NaN);
return;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 507c60f..17ef3c0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -36,6 +36,7 @@
private final int mAreaId;
private final int mPropertyId;
private final String mTempFormat;
+ private boolean mDisplayFahrenheit = false;
public TemperatureTextView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -57,9 +58,17 @@
setText("--");
return;
}
+ if (mDisplayFahrenheit) {
+ temp = convertToFahrenheit(temp);
+ }
setText(String.format(mTempFormat, temp));
}
+ @Override
+ public void setDisplayInFahrenheit(boolean displayFahrenheit) {
+ mDisplayFahrenheit = displayFahrenheit;
+ }
+
/**
* @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
*/
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
index 7651356..c17da18 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
@@ -23,10 +23,26 @@
/**
* Formats the float for display
*
- * @param temp - The current temp or NaN
+ * @param temp - The current temp in Celsius or NaN
*/
void setTemp(float temp);
+ /**
+ * Render the displayed temperature in Fahrenheit
+ *
+ * @param displayFahrenheit - True if temperature should be displayed in Fahrenheit
+ */
+ void setDisplayInFahrenheit(boolean displayFahrenheit);
+
+ /**
+ * Convert the given temperature in Celsius into Fahrenheit
+ *
+ * @param realTemp - The temperature in Celsius
+ * @return Temperature in Fahrenheit.
+ */
+ default float convertToFahrenheit(float realTemp) {
+ return (realTemp * 9f / 5f) + 32;
+ }
/**
* @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
index 0467bff..76126fc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
@@ -99,6 +99,7 @@
private final TemperatureColorStore mColorStore = new TemperatureColorStore();
private final TemperatureBackgroundAnimator mBackgroundAnimator;
private final TemperatureTextAnimator mTextAnimator;
+ boolean mDisplayInFahrenheit = false;
public AnimatedTemperatureView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -193,6 +194,9 @@
*/
@Override
public void setTemp(float temp) {
+ if (mDisplayInFahrenheit) {
+ temp = convertToFahrenheit(temp);
+ }
mTextAnimator.setTemp(temp);
if (Float.isNaN(temp)) {
mBackgroundAnimator.hideCircle();
@@ -219,6 +223,11 @@
mBackgroundAnimator.animateOpen();
}
+ @Override
+ public void setDisplayInFahrenheit(boolean displayInFahrenheit) {
+ mDisplayInFahrenheit = displayInFahrenheit;
+ }
+
boolean isMinValue(float temp) {
return !Float.isNaN(mMinValue) && isApproxEqual(temp, mMinValue);
}
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 0be71e6..42885e8 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -25,7 +25,7 @@
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 133d8ba..af52c00 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -117,7 +117,7 @@
mPackageManager = ActivityThread.getPackageManager();
mSettings = mSettingsFactory.createAndRegister(mHandler,
getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
- mSmartActionsHelper = new SmartActionsHelper();
+ mSmartActionsHelper = new SmartActionsHelper(getContext(), mSettings);
mNotificationCategorizer = new NotificationCategorizer();
mAgingHelper = new AgingHelper(getContext(),
mNotificationCategorizer,
@@ -215,10 +215,8 @@
return null;
}
NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel);
- ArrayList<Notification.Action> actions =
- mSmartActionsHelper.suggestActions(this, entry, mSettings);
- ArrayList<CharSequence> replies =
- mSmartActionsHelper.suggestReplies(this, entry, mSettings);
+ ArrayList<Notification.Action> actions = mSmartActionsHelper.suggestActions(entry);
+ ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(entry);
return createEnqueuedNotificationAdjustment(entry, actions, replies);
}
@@ -294,7 +292,7 @@
synchronized (mkeyToImpressions) {
ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
createChannelImpressionsWithThresholds());
- if (stats.hasSeen()) {
+ if (stats != null && stats.hasSeen()) {
ci.incrementViews();
updatedImpressions = true;
}
@@ -343,6 +341,7 @@
if (entry != null) {
entry.setSeen();
mAgingHelper.onNotificationSeen(entry);
+ mSmartActionsHelper.onNotificationSeen(entry);
}
}
} catch (Throwable e) {
@@ -351,34 +350,46 @@
}
@Override
- public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ public void onNotificationExpansionChanged(@NonNull String key, boolean isUserAction,
boolean isExpanded) {
if (DEBUG) {
- Log.i(TAG,
- "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
- + ", isExpanded = isExpanded");
+ Log.d(TAG, "onNotificationExpansionChanged() called with: key = [" + key
+ + "], isUserAction = [" + isUserAction + "], isExpanded = [" + isExpanded
+ + "]");
+ }
+ NotificationEntry entry = mLiveNotifications.get(key);
+
+ if (entry != null) {
+ entry.setExpanded(isExpanded);
+ mSmartActionsHelper.onNotificationExpansionChanged(entry, isUserAction, isExpanded);
}
}
@Override
- public void onNotificationDirectReply(String key) {
+ public void onNotificationDirectReply(@NonNull String key) {
if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+ mSmartActionsHelper.onNotificationDirectReply(key);
}
@Override
- public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
+ @Source int source) {
if (DEBUG) {
Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
+ "], source = [" + source + "]");
}
+ mSmartActionsHelper.onSuggestedReplySent(key, reply, source);
}
@Override
- public void onActionClicked(String key, Notification.Action action, int source) {
+ public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+ @Source int source) {
if (DEBUG) {
- Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title
- + "], source = [" + source + "]");
+ Log.d(TAG,
+ "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+ + "], source = [" + source + "]");
}
+ mSmartActionsHelper.onActionClicked(key, action, source);
}
@Override
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
index 6f437bd5..71fd9ce 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
@@ -52,6 +52,8 @@
private NotificationChannel mChannel;
private int mImportance;
private boolean mSeen;
+ private boolean mExpanded;
+ private boolean mIsShowActionEventLogged;
public NotificationEntry(IPackageManager packageManager, StatusBarNotification sbn,
NotificationChannel channel) {
@@ -216,10 +218,26 @@
mSeen = true;
}
+ public void setExpanded(boolean expanded) {
+ mExpanded = expanded;
+ }
+
+ public void setShowActionEventLogged() {
+ mIsShowActionEventLogged = true;
+ }
+
public boolean hasSeen() {
return mSeen;
}
+ public boolean isExpanded() {
+ return mExpanded;
+ }
+
+ public boolean isShowActionEventLogged() {
+ return mIsShowActionEventLogged;
+ }
+
public StatusBarNotification getSbn() {
return mSbn;
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 38df9b0..b041842 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -24,12 +24,16 @@
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
+import android.service.notification.NotificationAssistantService;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.LruCache;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLinks;
import java.time.Instant;
@@ -47,6 +51,7 @@
private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>();
private static final ArrayList<CharSequence> EMPTY_REPLY_LIST = new ArrayList<>();
+ private static final String KEY_ACTION_TYPE = "action_type";
// If a notification has any of these flags set, it's inelgibile for actions being added.
private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
Notification.FLAG_ONGOING_EVENT
@@ -59,6 +64,7 @@
private static final int MAX_SUGGESTED_REPLIES = 3;
// TODO: Make this configurable.
private static final int MAX_MESSAGES_TO_EXTRACT = 5;
+ private static final int MAX_RESULT_ID_TO_CACHE = 20;
private static final ConversationActions.TypeConfig TYPE_CONFIG =
new ConversationActions.TypeConfig.Builder().setIncludedTypes(
@@ -68,26 +74,36 @@
private static final List<String> HINTS =
Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION);
- SmartActionsHelper() {
+ private Context mContext;
+ @Nullable
+ private TextClassifier mTextClassifier;
+ @NonNull
+ private AssistantSettings mSettings;
+ private LruCache<String, String> mNotificationKeyToResultIdCache =
+ new LruCache<>(MAX_RESULT_ID_TO_CACHE);
+
+ SmartActionsHelper(Context context, AssistantSettings settings) {
+ mContext = context;
+ TextClassificationManager textClassificationManager =
+ mContext.getSystemService(TextClassificationManager.class);
+ if (textClassificationManager != null) {
+ mTextClassifier = textClassificationManager.getTextClassifier();
+ }
+ mSettings = settings;
}
/**
* Adds action adjustments based on the notification contents.
*/
@NonNull
- ArrayList<Notification.Action> suggestActions(@Nullable Context context,
- @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
- if (!settings.mGenerateActions) {
+ ArrayList<Notification.Action> suggestActions(@NonNull NotificationEntry entry) {
+ if (!mSettings.mGenerateActions) {
return EMPTY_ACTION_LIST;
}
if (!isEligibleForActionAdjustment(entry)) {
return EMPTY_ACTION_LIST;
}
- if (context == null) {
- return EMPTY_ACTION_LIST;
- }
- TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
- if (tcm == null) {
+ if (mTextClassifier == null) {
return EMPTY_ACTION_LIST;
}
List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
@@ -96,22 +112,17 @@
}
// TODO: Move to TextClassifier.suggestConversationActions once it is ready.
return suggestActionsFromText(
- tcm, messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS);
+ messages.get(messages.size() - 1).getText(), MAX_SMART_ACTIONS);
}
- ArrayList<CharSequence> suggestReplies(@Nullable Context context,
- @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
- if (!settings.mGenerateReplies) {
+ ArrayList<CharSequence> suggestReplies(@NonNull NotificationEntry entry) {
+ if (!mSettings.mGenerateReplies) {
return EMPTY_REPLY_LIST;
}
if (!isEligibleForReplyAdjustment(entry)) {
return EMPTY_REPLY_LIST;
}
- if (context == null) {
- return EMPTY_REPLY_LIST;
- }
- TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
- if (tcm == null) {
+ if (mTextClassifier == null) {
return EMPTY_REPLY_LIST;
}
List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
@@ -125,14 +136,122 @@
.setTypeConfig(TYPE_CONFIG)
.build();
- TextClassifier textClassifier = tcm.getTextClassifier();
+ ConversationActions conversationActionsResult =
+ mTextClassifier.suggestConversationActions(request);
List<ConversationActions.ConversationAction> conversationActions =
- textClassifier.suggestConversationActions(request).getConversationActions();
-
- return conversationActions.stream()
+ conversationActionsResult.getConversationActions();
+ ArrayList<CharSequence> replies = conversationActions.stream()
.map(conversationAction -> conversationAction.getTextReply())
.filter(textReply -> !TextUtils.isEmpty(textReply))
.collect(Collectors.toCollection(ArrayList::new));
+
+ String resultId = conversationActionsResult.getId();
+ if (resultId != null && !replies.isEmpty()) {
+ mNotificationKeyToResultIdCache.put(entry.getSbn().getKey(), resultId);
+ }
+ return replies;
+ }
+
+ void onNotificationSeen(@NonNull NotificationEntry entry) {
+ if (entry.isExpanded()) {
+ maybeSendActionShownEvent(entry);
+ }
+ }
+
+ void onNotificationExpansionChanged(@NonNull NotificationEntry entry, boolean isUserAction,
+ boolean isExpanded) {
+ // Notification can be expanded in the background, and thus the isUserAction check.
+ if (isUserAction && isExpanded) {
+ maybeSendActionShownEvent(entry);
+ }
+ }
+
+ void onNotificationDirectReply(@NonNull String key) {
+ if (mTextClassifier == null) {
+ return;
+ }
+ String resultId = mNotificationKeyToResultIdCache.get(key);
+ if (resultId == null) {
+ return;
+ }
+ TextClassifierEvent textClassifierEvent =
+ createTextClassifierEventBuilder(TextClassifierEvent.TYPE_MANUAL_REPLY, resultId)
+ .build();
+ mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ }
+
+ void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
+ @NotificationAssistantService.Source int source) {
+ if (mTextClassifier == null) {
+ return;
+ }
+ if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
+ return;
+ }
+ String resultId = mNotificationKeyToResultIdCache.get(key);
+ if (resultId == null) {
+ return;
+ }
+ TextClassifierEvent textClassifierEvent =
+ createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
+ .setEntityType(ConversationActions.TYPE_TEXT_REPLY)
+ .build();
+ mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ }
+
+ void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+ @NotificationAssistantService.Source int source) {
+ if (mTextClassifier == null) {
+ return;
+ }
+ if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
+ return;
+ }
+ String resultId = mNotificationKeyToResultIdCache.get(key);
+ if (resultId == null) {
+ return;
+ }
+ String actionType = action.getExtras().getString(KEY_ACTION_TYPE);
+ if (actionType == null) {
+ return;
+ }
+ TextClassifierEvent textClassifierEvent =
+ createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
+ .setEntityType(actionType)
+ .build();
+ mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ }
+
+ private TextClassifierEvent.Builder createTextClassifierEventBuilder(
+ int eventType, @NonNull String resultId) {
+ return new TextClassifierEvent.Builder(
+ TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType)
+ .setEventTime(System.currentTimeMillis())
+ .setEventContext(
+ new TextClassificationContext.Builder(
+ mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION)
+ .build())
+ .setResultId(resultId);
+ }
+
+ private void maybeSendActionShownEvent(@NonNull NotificationEntry entry) {
+ if (mTextClassifier == null) {
+ return;
+ }
+ String resultId = mNotificationKeyToResultIdCache.get(entry.getSbn().getKey());
+ if (resultId == null) {
+ return;
+ }
+ // Only report if this is the first time the user sees these suggestions.
+ if (entry.isShowActionEventLogged()) {
+ return;
+ }
+ entry.setShowActionEventLogged();
+ TextClassifierEvent textClassifierEvent =
+ createTextClassifierEventBuilder(TextClassifierEvent.TYPE_ACTIONS_SHOWN, resultId)
+ .build();
+ // TODO: If possible, report which replies / actions are actually seen by user.
+ mTextClassifier.onTextClassifierEvent(textClassifierEvent);
}
/**
@@ -220,13 +339,10 @@
/** Returns a list of actions to act on entities in a given piece of text. */
@NonNull
private ArrayList<Notification.Action> suggestActionsFromText(
- @NonNull TextClassificationManager tcm, @Nullable CharSequence text,
- int maxSmartActions) {
+ @Nullable CharSequence text, int maxSmartActions) {
if (TextUtils.isEmpty(text)) {
return EMPTY_ACTION_LIST;
}
- TextClassifier textClassifier = tcm.getTextClassifier();
-
// We want to process only text visible to the user to avoid confusing suggestions, so we
// truncate the text to a reasonable length. This is particularly important for e.g.
// email apps that sometimes include the text for the entire thread.
@@ -239,7 +355,7 @@
Collections.singletonList(
TextClassifier.HINT_TEXT_IS_NOT_EDITABLE)))
.build();
- TextLinks links = textClassifier.generateLinks(textLinksRequest);
+ TextLinks links = mTextClassifier.generateLinks(textLinksRequest);
ArrayMap<String, Integer> entityTypeFrequency = getEntityTypeFrequency(links);
ArrayList<Notification.Action> actions = new ArrayList<>();
@@ -254,19 +370,26 @@
// Generate the actions, and add the most prominent ones to the action bar.
TextClassification classification =
- textClassifier.classifyText(
+ mTextClassifier.classifyText(
new TextClassification.Request.Builder(
text, link.getStart(), link.getEnd()).build());
+ if (classification.getEntityCount() == 0) {
+ continue;
+ }
int numOfActions = Math.min(
MAX_ACTIONS_PER_LINK, classification.getActions().size());
for (int i = 0; i < numOfActions; ++i) {
- RemoteAction action = classification.getActions().get(i);
- actions.add(
- new Notification.Action.Builder(
- action.getIcon(),
- action.getTitle(),
- action.getActionIntent())
- .build());
+ RemoteAction remoteAction = classification.getActions().get(i);
+ Notification.Action action = new Notification.Action.Builder(
+ remoteAction.getIcon(),
+ remoteAction.getTitle(),
+ remoteAction.getActionIntent())
+ .setSemanticAction(
+ Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+ .addExtras(Bundle.forPair(KEY_ACTION_TYPE, classification.getEntity(0)))
+ .build();
+ actions.add(action);
+
// We have enough smart actions.
if (actions.size() >= maxSmartActions) {
return actions;
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index 0352ebc..da382a0 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -28,12 +28,14 @@
import android.app.Person;
import android.content.Context;
import android.os.Process;
+import android.service.notification.NotificationAssistantService;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.Subject;
@@ -44,12 +46,14 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -58,8 +62,14 @@
@RunWith(AndroidJUnit4.class)
public class SmartActionHelperTest {
+ private static final String NOTIFICATION_KEY = "key";
+ private static final String RESULT_ID = "id";
- private SmartActionsHelper mSmartActionsHelper = new SmartActionsHelper();
+ private static final ConversationActions.ConversationAction REPLY_ACTION =
+ new ConversationActions.ConversationAction.Builder(
+ ConversationActions.TYPE_TEXT_REPLY).setTextReply("Home").build();
+
+ private SmartActionsHelper mSmartActionsHelper;
private Context mContext;
@Mock private TextClassifier mTextClassifier;
@Mock private NotificationEntry mNotificationEntry;
@@ -75,7 +85,7 @@
mContext.getSystemService(TextClassificationManager.class)
.setTextClassifier(mTextClassifier);
when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(new ConversationActions(Collections.emptyList(), null));
+ .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID));
when(mNotificationEntry.getSbn()).thenReturn(mStatusBarNotification);
// The notification is eligible to have smart suggestions.
@@ -83,18 +93,20 @@
when(mNotificationEntry.isMessaging()).thenReturn(true);
when(mStatusBarNotification.getPackageName()).thenReturn("random.app");
when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle());
+ when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY);
mNotificationBuilder = new Notification.Builder(mContext, "channel");
mSettings = AssistantSettings.createForTesting(
null, null, Process.myUserHandle().getIdentifier(), null);
mSettings.mGenerateActions = true;
mSettings.mGenerateReplies = true;
+ mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings);
}
@Test
public void testSuggestReplies_notMessagingApp() {
when(mNotificationEntry.isMessaging()).thenReturn(false);
ArrayList<CharSequence> textReplies =
- mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
assertThat(textReplies).isEmpty();
}
@@ -102,7 +114,7 @@
public void testSuggestReplies_noInlineReply() {
when(mNotificationEntry.hasInlineReply()).thenReturn(false);
ArrayList<CharSequence> textReplies =
- mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
assertThat(textReplies).isEmpty();
}
@@ -169,18 +181,128 @@
.build();
when(mNotificationEntry.getNotification()).thenReturn(notification);
- mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
verify(mTextClassifier, never())
.suggestConversationActions(any(ConversationActions.Request.class));
}
+ @Test
+ public void testOnSuggestedReplySent() {
+ final String message = "Where are you?";
+ Notification notification = mNotificationBuilder.setContentText(message).build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
+ mSmartActionsHelper.onSuggestedReplySent(
+ NOTIFICATION_KEY, message, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+
+ ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+ ArgumentCaptor.forClass(TextClassifierEvent.class);
+ verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+ TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+ assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_SMART_ACTION);
+ }
+
+ @Test
+ public void testOnSuggestedReplySent_anotherNotification() {
+ final String message = "Where are you?";
+ Notification notification = mNotificationBuilder.setContentText(message).build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
+ mSmartActionsHelper.onSuggestedReplySent(
+ "something_else", message, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+
+ verify(mTextClassifier, never())
+ .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class));
+ }
+
+ @Test
+ public void testOnSuggestedReplySent_missingResultId() {
+ when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
+ .thenReturn(new ConversationActions(Collections.emptyList(), null));
+
+ final String message = "Where are you?";
+ Notification notification = mNotificationBuilder.setContentText(message).build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
+ mSmartActionsHelper.onSuggestedReplySent(
+ "something_else", message, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
+
+ verify(mTextClassifier, never())
+ .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class));
+ }
+
+ @Test
+ public void testOnNotificationDirectReply() {
+ Notification notification = mNotificationBuilder.setContentText("Where are you?").build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
+ mSmartActionsHelper.onNotificationDirectReply(NOTIFICATION_KEY);
+
+ ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+ ArgumentCaptor.forClass(TextClassifierEvent.class);
+ verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+ TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+ assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_MANUAL_REPLY);
+ }
+
+ @Test
+ public void testOnNotificationExpansionChanged() {
+ final String message = "Where are you?";
+ Notification notification = mNotificationBuilder.setContentText(message).build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
+ mSmartActionsHelper.onNotificationExpansionChanged(mNotificationEntry, true, true);
+
+ ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+ ArgumentCaptor.forClass(TextClassifierEvent.class);
+ verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+ TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+ assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN);
+ }
+
+ @Test
+ public void testOnNotificationsSeen_notExpanded() {
+ final String message = "Where are you?";
+ Notification notification = mNotificationBuilder.setContentText(message).build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+ when(mNotificationEntry.isExpanded()).thenReturn(false);
+
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
+ mSmartActionsHelper.onNotificationSeen(mNotificationEntry);
+
+ verify(mTextClassifier, never()).onTextClassifierEvent(
+ Mockito.any(TextClassifierEvent.class));
+ }
+
+ @Test
+ public void testOnNotificationsSeen_expanded() {
+ final String message = "Where are you?";
+ Notification notification = mNotificationBuilder.setContentText(message).build();
+ when(mNotificationEntry.getNotification()).thenReturn(notification);
+ when(mNotificationEntry.isExpanded()).thenReturn(true);
+
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
+ mSmartActionsHelper.onNotificationSeen(mNotificationEntry);
+
+ ArgumentCaptor<TextClassifierEvent> argumentCaptor =
+ ArgumentCaptor.forClass(TextClassifierEvent.class);
+ verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
+ TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
+ assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN);
+ }
+
private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault());
}
private List<ConversationActions.Message> getMessagesInRequest() {
- mSmartActionsHelper.suggestReplies(mContext, mNotificationEntry, mSettings);
+ mSmartActionsHelper.suggestReplies(mNotificationEntry);
ArgumentCaptor<ConversationActions.Request> argumentCaptor =
ArgumentCaptor.forClass(ConversationActions.Request.class);
@@ -189,6 +311,17 @@
return request.getConversation();
}
+ private void assertTextClassifierEvent(
+ TextClassifierEvent textClassifierEvent, int expectedEventType) {
+ assertThat(textClassifierEvent.getEventCategory())
+ .isEqualTo(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS);
+ assertThat(textClassifierEvent.getEventContext().getPackageName())
+ .isEqualTo(InstrumentationRegistry.getTargetContext().getPackageName());
+ assertThat(textClassifierEvent.getEventContext().getWidgetType())
+ .isEqualTo(TextClassifier.WIDGET_TYPE_NOTIFICATION);
+ assertThat(textClassifierEvent.getEventType()).isEqualTo(expectedEventType);
+ }
+
private static final class MessageSubject
extends Subject<MessageSubject, ConversationActions.Message> {
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1eb4b74..8d04702 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -67,7 +67,7 @@
private static final boolean DEBUG = false;
- public static final String AUTHORITY = "com.android.externalstorage.documents";
+ public static final String AUTHORITY = DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
private static final Uri BASE_URI =
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
@@ -96,7 +96,8 @@
public boolean reportAvailableBytes = true;
}
- private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+ private static final String ROOT_ID_PRIMARY_EMULATED =
+ DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
private static final String ROOT_ID_HOME = "home";
private StorageManager mStorageManager;
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
index 83427d4..04cf01a 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -14,7 +14,7 @@
],
static_libs: [
- "android-support-test",
+ "androidx.test.rules",
"mockito-target",
"truth-prebuilt",
],
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
index 58b6e86..f1a6af0 100644
--- a/packages/ExternalStorageProvider/tests/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
@@ -6,7 +6,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.externalstorage"
android:label="ExternalStorageProvider Tests" />
</manifest>
diff --git a/packages/ExternalStorageProvider/tests/AndroidTest.xml b/packages/ExternalStorageProvider/tests/AndroidTest.xml
index e5fa73f..f8438b2 100644
--- a/packages/ExternalStorageProvider/tests/AndroidTest.xml
+++ b/packages/ExternalStorageProvider/tests/AndroidTest.xml
@@ -23,7 +23,7 @@
<option name="test-tag" value="ExternalStorageProviderTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.externalstorage.tests" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
index a88b3e1..fbf2e4b 100644
--- a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
+++ b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
@@ -23,8 +23,9 @@
import static org.mockito.Mockito.verify;
import android.content.pm.ProviderInfo;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/MtpDocumentsProvider/perf_tests/Android.mk b/packages/MtpDocumentsProvider/perf_tests/Android.mk
index e873157..d2f1c7a 100644
--- a/packages/MtpDocumentsProvider/perf_tests/Android.mk
+++ b/packages/MtpDocumentsProvider/perf_tests/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
LOCAL_PACKAGE_NAME := MtpDocumentsProviderPerfTests
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
diff --git a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
index 26e109d..4367652 100644
--- a/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/perf_tests/AndroidManifest.xml
@@ -7,7 +7,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.mtp"
android:label="Performance tests for MtpDocumentsProvider." />
</manifest>
diff --git a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
index 36f6fe9..58b9c67 100644
--- a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
+++ b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
@@ -23,17 +23,15 @@
import android.os.ProxyFileDescriptorCallback;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
-import android.system.Os;
-import android.support.test.filters.LargeTest;
-import android.support.test.InstrumentationRegistry;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import libcore.io.IoUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.junit.Test;
+
+import java.io.IOException;
@RunWith(JUnit4.class)
public class AppFusePerfTest {
diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
index 0312b81..28b05396 100644
--- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
+++ b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
@@ -39,12 +39,14 @@
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
import com.android.hotspot2.R;
import java.net.MalformedURLException;
import java.net.URL;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
/**
* Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2.
@@ -252,6 +254,10 @@
@Override
public void onReceivedError(WebView view, WebResourceRequest request,
WebResourceError error) {
+ if (request.getUrl().toString().startsWith("http://127.0.0.1")) {
+ view.stopLoading();
+ }
+
if (request.isForMainFrame()) {
// This happens right after getting HTTP redirect response from an OSU server
// since no more Http request is allowed to send to the OSU server.
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 1e0ff50..a05a219 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -121,7 +121,7 @@
<string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
<!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
<string name="uninstall_remove_contributed_files">Also remove <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of associated media files.</string>
- <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] -->
+ <!-- Label of a checkbox that allows to keep the data (e.g. files, settings) of the app on uninstall [CHAR LIMIT=none] -->
<string name="uninstall_keep_data">Keep <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of app data.</string>
<!-- Label for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] -->
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp
index e88074e..c6dc263 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.bp
+++ b/packages/PrintSpooler/tests/outofprocess/Android.bp
@@ -19,7 +19,7 @@
libs: ["android.test.runner.stubs"],
static_libs: [
- "android-support-test",
+ "androidx.test.rules",
"ub-uiautomator",
"mockito-target-minus-junit4",
"print-test-util-lib",
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
index 307cc93..fdcaa52 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
@@ -54,7 +54,7 @@
</application>
<!-- This runs in its own process, hence it instruments itself -->
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.printspooler.outofprocess.tests"
android:label="PrintSpooler Out of Process Test Cases">
</instrumentation>
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
index d21a2e4..b649e82 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
@@ -24,7 +24,7 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.printspooler.outofprocess.tests" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 7ebf93d..61c2f54 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -35,7 +35,6 @@
import android.print.test.services.FirstPrintService;
import android.print.test.services.PrinterDiscoverySessionCallbacks;
import android.print.test.services.StubbablePrinterDiscoverySession;
-import android.support.test.filters.LargeTest;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -43,6 +42,8 @@
import android.support.test.uiautomator.Until;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index 89ebf4d..305862a 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -31,7 +31,7 @@
import java.util.Arrays;
/**
- * This BarChartPreference shows four bar views in this preference at most.
+ * This BarChartPreference shows up to four bar views in this preference at most.
*
* <p>The following code sample shows a typical use, with an XML layout and code to initialize the
* contents of the BarChartPreference:
@@ -74,71 +74,28 @@
};
private int mMaxBarHeight;
- private @StringRes int mTitleId;
- private @StringRes int mDetailsId;
+ @StringRes
+ private int mTitleId;
+ @StringRes
+ private int mDetailsId;
private BarViewInfo[] mBarViewsInfo;
private View.OnClickListener mDetailsOnClickListener;
- /**
- * Constructs a new BarChartPreference with the given context's theme.
- * It sets a layout with settings bar chart style
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- */
public BarChartPreference(Context context) {
super(context);
init();
}
- /**
- * Constructs a new BarChartPreference with the given context's theme and the supplied
- * attribute set.
- * It sets a layout with settings bar chart style
- *
- * @param context the Context the view is running in
- * @param attrs the attributes of the XML tag that is inflating the view.
- */
public BarChartPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
- /**
- * Constructs a new BarChartPreference with the given context's theme, the supplied
- * attribute set, and default style attribute.
- * It sets a layout with settings bar chart style
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- * @param attrs The attributes of the XML tag that is inflating the view.
- * @param defStyleAttr An attribute in the current theme that contains a
- * reference to a style resource that supplies default
- * values for the view. Can be 0 to not look for
- * defaults.
- */
public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
- /**
- * Constructs a new BarChartPreference with the given context's theme, the supplied
- * attribute set, and default styles.
- * It sets a layout with settings bar chart style
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- * @param attrs The attributes of the XML tag that is inflating the view.
- * @param defStyleAttr An attribute in the current theme that contains a
- * reference to a style resource that supplies default
- * values for the view. Can be 0 to not look for
- * defaults.
- * @param defStyleRes A resource identifier of a style resource that
- * supplies default values for the view, used only if
- * defStyleAttr is 0 or can not be found in the theme.
- * Can be 0 to not look for defaults.
- */
public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
@@ -172,8 +129,6 @@
/**
* Set all bar view information which you'd like to show in preference.
*
- * <p>This method helps you do a sort by {@linkBarViewInfo#mBarNumber} in descending order.
- *
* @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
*/
public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
@@ -181,7 +136,7 @@
// Do a sort in descending order, the first element would have max {@link
// BarViewInfo#mBarNumber}
Arrays.sort(mBarViewsInfo);
- caculateAllBarViewsHeight();
+ calculateAllBarViewHeights();
notifyChanged();
}
@@ -210,8 +165,12 @@
private void bindChartDetailsView(PreferenceViewHolder holder) {
final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
- detailsView.setText(mDetailsId);
- detailsView.setOnClickListener(mDetailsOnClickListener);
+ if (mDetailsId == 0) {
+ detailsView.setVisibility(View.GONE);
+ } else {
+ detailsView.setText(mDetailsId);
+ detailsView.setOnClickListener(mDetailsOnClickListener);
+ }
}
private void updateBarChart(PreferenceViewHolder holder) {
@@ -224,19 +183,19 @@
continue;
}
barView.setVisibility(View.VISIBLE);
- barView.updateBarViewUI(mBarViewsInfo[index]);
+ barView.updateView(mBarViewsInfo[index]);
}
}
- private void caculateAllBarViewsHeight() {
+ private void calculateAllBarViewHeights() {
// Since we sorted this array in advance, the first element must have the max {@link
- // BarViewInfo#mBarNumber}.
- final int maxBarViewNumber = mBarViewsInfo[0].getBarNumber();
- // If the max number of bar view is zero, then we don't caculate the unit for bar height.
- final int unit = maxBarViewNumber == 0 ? 0 : mMaxBarHeight / maxBarViewNumber;
+ // BarViewInfo#mHeight}.
+ final int maxBarHeight = mBarViewsInfo[0].getHeight();
+ // If the max number of bar view is zero, then we don't calculate the unit for bar height.
+ final int unit = maxBarHeight == 0 ? 0 : mMaxBarHeight / maxBarHeight;
for (BarViewInfo barView : mBarViewsInfo) {
- barView.setBarHeight(barView.getBarNumber() * unit);
+ barView.setNormalizedHeight(barView.getHeight() * unit);
}
}
}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
index 6243a2d..6bf61ae 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -30,7 +30,7 @@
import androidx.annotation.VisibleForTesting;
/**
- * A extension view for bar chart.
+ * {@link View} for a single vertical bar with icon and summary.
*/
public class BarView extends LinearLayout {
@@ -41,24 +41,11 @@
private TextView mBarTitle;
private TextView mBarSummary;
- /**
- * Constructs a new BarView with the given context's theme.
- *
- * @param context The Context the view is running in, through which it can
- * access the current theme, resources, etc.
- */
public BarView(Context context) {
super(context);
init();
}
- /**
- * Constructs a new BarView with the given context's theme and the supplied
- * attribute set.
- *
- * @param context the Context the view is running in
- * @param attrs the attributes of the XML tag that is inflating the view.
- */
public BarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
@@ -77,17 +64,16 @@
}
/**
- * This helps update the bar view UI with a {@link BarViewInfo}.
- *
- * @param barViewInfo A {@link BarViewInfo} saves bar view status.
+ * Updates the view with a {@link BarViewInfo}.
*/
- public void updateBarViewUI(BarViewInfo barViewInfo) {
+ void updateView(BarViewInfo barViewInfo) {
+ setOnClickListener(barViewInfo.getClickListener());
//Set height of bar view
- mBarView.getLayoutParams().height = barViewInfo.getBarHeight();
+ mBarView.getLayoutParams().height = barViewInfo.getNormalizedHeight();
mIcon.setImageDrawable(barViewInfo.getIcon());
// For now, we use the bar number as title.
- mBarTitle.setText(Integer.toString(barViewInfo.getBarNumber()));
- mBarSummary.setText(barViewInfo.getSummaryRes());
+ mBarTitle.setText(Integer.toString(barViewInfo.getHeight()));
+ mBarSummary.setText(barViewInfo.getSummary());
}
@VisibleForTesting
@@ -106,9 +92,9 @@
setGravity(Gravity.CENTER);
mBarView = findViewById(R.id.bar_view);
- mIcon = (ImageView) findViewById(R.id.icon_view);
- mBarTitle = (TextView) findViewById(R.id.bar_title);
- mBarSummary = (TextView) findViewById(R.id.bar_summary);
+ mIcon = findViewById(R.id.icon_view);
+ mBarTitle = findViewById(R.id.bar_title);
+ mBarSummary = findViewById(R.id.bar_summary);
}
private void setOnClickListner(View.OnClickListener listener) {
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
index aa83ce9..409f9ea 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -31,116 +31,71 @@
public class BarViewInfo implements Comparable<BarViewInfo> {
private final Drawable mIcon;
- private View.OnClickListener mListener;
- private @StringRes int mSummaryRes;
+ private View.OnClickListener mClickListener;
+ @StringRes
+ private int mSummary;
// A number indicates this bar's height. The larger number shows a higher bar view.
- private int mBarNumber;
+ private int mHeight;
// A real height of bar view.
- private int mBarHeight;
+ private int mNormalizedHeight;
/**
* Construct a BarViewInfo instance.
*
- * @param icon the icon of bar view.
- * @param barNumber the number of bar view. The larger number show a more height of bar view.
- * @param summaryRes the resource identifier of the string resource to be displayed
- * @return BarViewInfo object.
+ * @param icon The icon of bar view.
+ * @param barHeight The height of bar view. Larger number shows a higher bar view.
+ * @param summary The string resource id for summary.
*/
- public BarViewInfo(Drawable icon, @IntRange(from = 0) int barNumber,
- @StringRes int summaryRes) {
+ public BarViewInfo(Drawable icon, @IntRange(from = 0) int barHeight, @StringRes int summary) {
mIcon = icon;
- mBarNumber = barNumber;
- mSummaryRes = summaryRes;
+ mHeight = barHeight;
+ mSummary = summary;
}
/**
- * Set number for bar view.
- *
- * @param barNumber the number of bar view. The larger number shows a higher bar view.
- */
- public void setBarNumber(@IntRange(from = 0) int barNumber) {
- mBarNumber = barNumber;
- }
-
- /**
- * Set summary resource for bar view
- *
- * @param resId the resource identifier of the string resource to be displayed
- */
- public void setSummary(@StringRes int resId) {
- mSummaryRes = resId;
- }
-
- /**
- * Set a click listner for bar view.
- *
- * @param listener the click listner is attached on bar view.
+ * Set a click listener for bar view.
*/
public void setClickListener(@Nullable View.OnClickListener listener) {
- mListener = listener;
- }
-
- /**
- * Get the icon of bar view.
- *
- * @return Drawable the icon of bar view.
- */
- public Drawable getIcon() {
- return mIcon;
- }
-
- /**
- * Get the OnClickListener of bar view.
- *
- * @return View.OnClickListener the click listner of bar view.
- */
- public View.OnClickListener getListener() {
- return mListener;
- }
-
- /**
- * Get the real height of bar view.
- *
- * @return the real height of bar view.
- */
- public int getBarHeight() {
- return mBarHeight;
- }
-
- /**
- * Get summary resource of bar view.
- *
- * @return summary resource of bar view.
- */
- public int getSummaryRes() {
- return mSummaryRes;
- }
-
- /**
- * Get the number of app uses this permisssion.
- *
- * @return the number of app uses this permission.
- */
- public int getBarNumber() {
- return mBarNumber;
+ mClickListener = listener;
}
@Override
public int compareTo(BarViewInfo other) {
// Descending order
- return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mBarNumber)
+ return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mHeight)
.compare(other, this);
}
- /**
- * Set a real height for bar view.
- *
- * <p>This method should not be called by outside. It usually should be called by
- * {@link BarChartPreference#caculateAllBarViewsHeight}
- *
- * @param barHeight the real bar height for bar view.
- */
- void setBarHeight(@IntRange(from = 0) int barHeight) {
- mBarHeight = barHeight;
+ void setHeight(@IntRange(from = 0) int height) {
+ mHeight = height;
+ }
+
+ void setSummary(@StringRes int resId) {
+ mSummary = resId;
+ }
+
+ Drawable getIcon() {
+ return mIcon;
+ }
+
+ int getHeight() {
+ return mHeight;
+ }
+
+ View.OnClickListener getClickListener() {
+ return mClickListener;
+ }
+
+ @StringRes
+ int getSummary() {
+ return mSummary;
+ }
+
+ void setNormalizedHeight(@IntRange(from = 0) int barHeight) {
+ mNormalizedHeight = barHeight;
+ }
+
+ int getNormalizedHeight() {
+ return mNormalizedHeight;
}
}
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
index a046332..7e65848 100644
--- a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 2b7babd0..177ba00 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -321,6 +321,11 @@
// Dispatch device add callback to show bonded but
// not connected devices in discovery mode
dispatchDeviceAdded(cachedDevice);
+ Log.d(TAG, "DeviceFoundHandler found bonded and not connected device:"
+ + cachedDevice);
+ } else {
+ Log.d(TAG, "DeviceFoundHandler found existing CachedBluetoothDevice:"
+ + cachedDevice);
}
cachedDevice.setRssi(rssi);
cachedDevice.setJustDiscovered(true);
@@ -463,6 +468,16 @@
private class AclStateChangedHandler implements Handler {
@Override
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ if (device == null) {
+ Log.w(TAG, "AclStateChangedHandler: device is null");
+ return;
+ }
+
+ // Avoid to notify Settings UI for Hearing Aid sub device.
+ if (mDeviceManager.isSubDevice(device)) {
+ return;
+ }
+
final String action = intent.getAction();
if (action == null) {
Log.w(TAG, "AclStateChangedHandler: action is null");
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1bffff7..e28c894 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -116,8 +116,8 @@
void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
if (BluetoothUtils.D) {
- Log.d(TAG, "onProfileStateChanged: profile " + profile +
- " newProfileState " + newProfileState);
+ Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device=" + mDevice
+ + ", newProfileState " + newProfileState);
}
if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF)
{
@@ -570,7 +570,7 @@
}
if (BluetoothUtils.D) {
- Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
+ Log.e(TAG, "updating profiles for " + mDevice.getAliasName() + ", " + mDevice);
BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 3a62838..5b4a8b4f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -127,6 +127,25 @@
}
/**
+ * Search for existing sub device {@link CachedBluetoothDevice}.
+ *
+ * @param device the address of the Bluetooth device
+ * @return true for found sub device or false.
+ */
+ public synchronized boolean isSubDevice(BluetoothDevice device) {
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ if (!cachedDevice.getDevice().equals(device)) {
+ // Check sub devices if it exists
+ CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+ if (subDevice != null && subDevice.getDevice().equals(device)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Updates the Hearing Aid devices; specifically the HiSyncId's. This routine is called when the
* Hearing Aid Service is connected and the HiSyncId's are now available.
* @param LocalBluetoothProfileManager profileManager
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 9572fb3..20fe495 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -1,6 +1,8 @@
package com.android.settingslib.core;
import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -11,6 +13,8 @@
*/
public abstract class AbstractPreferenceController {
+ private static final String TAG = "AbstractPrefController";
+
protected final Context mContext;
public AbstractPreferenceController(Context context) {
@@ -22,6 +26,10 @@
*/
public void displayPreference(PreferenceScreen screen) {
final String prefKey = getPreferenceKey();
+ if (TextUtils.isEmpty(prefKey)) {
+ Log.w(TAG, "Skipping displayPreference because key is empty:" + getClass().getName());
+ return;
+ }
if (isAvailable()) {
setVisible(screen, prefKey, true /* visible */);
if (this instanceof Preference.OnPreferenceChangeListener) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index e535348..c039fcc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.media;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
@@ -33,6 +35,13 @@
BluetoothMediaDevice(Context context, CachedBluetoothDevice device) {
super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
mCachedDevice = device;
+ initDeviceRecord();
+ buildConnectedState(device);
+ }
+
+ private void buildConnectedState(CachedBluetoothDevice device) {
+ mIsConnected = device.isActiveDevice(BluetoothProfile.A2DP)
+ || device.isActiveDevice(BluetoothProfile.HEARING_AID);
}
@Override
@@ -52,9 +61,15 @@
}
@Override
+ public void notifyConnectedChanged() {
+ buildConnectedState(mCachedDevice);
+ }
+
+ @Override
public void connect() {
//TODO(b/117129183): add callback to notify LocalMediaManager connection state.
mIsConnected = mCachedDevice.setActive();
+ super.connect();
Log.d(TAG, "connect() device : " + getName() + ", is selected : " + mIsConnected);
}
@@ -70,4 +85,18 @@
public CachedBluetoothDevice getCachedDevice() {
return mCachedDevice;
}
+
+ @Override
+ protected boolean isCarKitDevice() {
+ final BluetoothClass bluetoothClass = mCachedDevice.getDevice().getBluetoothClass();
+ if (bluetoothClass != null) {
+ switch (bluetoothClass.getDeviceClass()) {
+ // Both are common CarKit class
+ case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+ case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
index 04188e9..a9fc434 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
@@ -30,6 +31,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
/**
@@ -39,8 +41,8 @@
private static final String TAG = "BluetoothMediaManager";
- private final DeviceAttributeChangeCallback mCachedDeviceCallback =
- new DeviceAttributeChangeCallback();
+ private final DeviceProfileNotReadyObserverCallback mObserverCallback =
+ new DeviceProfileNotReadyObserverCallback();
private LocalBluetoothManager mLocalBluetoothManager;
private LocalBluetoothProfileManager mProfileManager;
@@ -48,6 +50,10 @@
private MediaDevice mLastAddedDevice;
private MediaDevice mLastRemovedDevice;
+ private boolean mIsA2dpProfileReady = false;
+ private boolean mIsHearingAidProfileReady = false;
+ private Collection<CachedBluetoothDevice> mCachedDevices;
+
BluetoothMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
Notification notification) {
super(context, notification);
@@ -62,6 +68,18 @@
mLocalBluetoothManager.getEventManager().registerCallback(this);
buildBluetoothDeviceList();
dispatchDeviceListAdded();
+
+ // The profile may not ready when calling startScan().
+ // Device status are all disconnected since profiles are not ready to connected.
+ // In this case, we observe all devices in CachedDeviceManager.
+ // When one of these device is connected to profile, will call buildBluetoothDeviceList()
+ // again to find the connected devices.
+ if (!mIsA2dpProfileReady && !mIsHearingAidProfileReady) {
+ mCachedDevices = mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy();
+ for (CachedBluetoothDevice device : mCachedDevices) {
+ device.registerCallback(mObserverCallback);
+ }
+ }
}
private void buildBluetoothDeviceList() {
@@ -75,6 +93,7 @@
Log.w(TAG, "addConnectedA2dpDevices() a2dp profile is null!");
return;
}
+
final List<BluetoothDevice> devices = a2dpProfile.getConnectedDevices();
final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
mLocalBluetoothManager.getCachedDeviceManager();
@@ -95,6 +114,8 @@
addMediaDevice(cachedDevice);
}
}
+
+ mIsA2dpProfileReady = a2dpProfile.isProfileReady();
}
private void addConnectedHearingAidDevices() {
@@ -103,6 +124,7 @@
Log.w(TAG, "addConnectedA2dpDevices() hap profile is null!");
return;
}
+
final List<Long> devicesHiSyncIds = new ArrayList<>();
final List<BluetoothDevice> devices = hapProfile.getConnectedDevices();
final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
@@ -128,13 +150,14 @@
addMediaDevice(cachedDevice);
}
}
+
+ mIsHearingAidProfileReady = hapProfile.isProfileReady();
}
private void addMediaDevice(CachedBluetoothDevice cachedDevice) {
MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
if (mediaDevice == null) {
mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice);
- cachedDevice.registerCallback(mCachedDeviceCallback);
mLastAddedDevice = mediaDevice;
mMediaDevices.add(mediaDevice);
}
@@ -143,16 +166,6 @@
@Override
public void stopScan() {
mLocalBluetoothManager.getEventManager().unregisterCallback(this);
- unregisterCachedDeviceCallback();
- }
-
- private void unregisterCachedDeviceCallback() {
- for (MediaDevice device : mMediaDevices) {
- if (device instanceof BluetoothMediaDevice) {
- ((BluetoothMediaDevice) device).getCachedDevice()
- .unregisterCallback(mCachedDeviceCallback);
- }
- }
}
@Override
@@ -164,8 +177,6 @@
final List<MediaDevice> removeDevicesList = new ArrayList<>();
for (MediaDevice device : mMediaDevices) {
if (device instanceof BluetoothMediaDevice) {
- ((BluetoothMediaDevice) device).getCachedDevice()
- .unregisterCallback(mCachedDeviceCallback);
removeDevicesList.add(device);
}
}
@@ -185,7 +196,7 @@
private boolean isCachedDeviceConnected(CachedBluetoothDevice cachedDevice) {
final boolean isConnectedHearingAidDevice = cachedDevice.isConnectedHearingAidDevice();
final boolean isConnectedA2dpDevice = cachedDevice.isConnectedA2dpDevice();
- Log.d(TAG, "isCachedDeviceConnected() cachedDevice : " + cachedDevice.getName()
+ Log.d(TAG, "isCachedDeviceConnected() cachedDevice : " + cachedDevice
+ ", is hearing aid connected : " + isConnectedHearingAidDevice
+ ", is a2dp connected : " + isConnectedA2dpDevice);
@@ -210,7 +221,6 @@
private void removeMediaDevice(CachedBluetoothDevice cachedDevice) {
final MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
if (mediaDevice != null) {
- cachedDevice.unregisterCallback(mCachedDeviceCallback);
mLastRemovedDevice = mediaDevice;
mMediaDevices.remove(mediaDevice);
}
@@ -226,7 +236,7 @@
@Override
public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
int bluetoothProfile) {
- Log.d(TAG, "onProfileConnectionStateChanged() device: " + cachedDevice.getName()
+ Log.d(TAG, "onProfileConnectionStateChanged() device: " + cachedDevice
+ ", state: " + state + ", bluetoothProfile: " + bluetoothProfile);
if (isCachedDeviceConnected(cachedDevice)) {
@@ -240,8 +250,7 @@
@Override
public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- Log.d(TAG, "onAclConnectionStateChanged() device: " + cachedDevice.getName()
- + ", state: " + state);
+ Log.d(TAG, "onAclConnectionStateChanged() device: " + cachedDevice + ", state: " + state);
if (isCachedDeviceConnected(cachedDevice)) {
addMediaDevice(cachedDevice);
@@ -252,10 +261,29 @@
}
}
- class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ Log.d(TAG, "onActiveDeviceChanged : device : "
+ + activeDevice + ", profile : " + bluetoothProfile);
+ if (BluetoothProfile.HEARING_AID == bluetoothProfile
+ || BluetoothProfile.A2DP == bluetoothProfile) {
+ final String id = activeDevice == null
+ ? PhoneMediaDevice.ID : MediaDeviceUtils.getId(activeDevice);
+ dispatchActiveDeviceChanged(id);
+ }
+ }
+
+ class DeviceProfileNotReadyObserverCallback implements CachedBluetoothDevice.Callback {
+
@Override
public void onDeviceAttributesChanged() {
- dispatchDeviceAttributesChanged();
+ if (!mIsA2dpProfileReady && !mIsHearingAidProfileReady) {
+ for (CachedBluetoothDevice device : mCachedDevices) {
+ device.unregisterCallback(mObserverCallback);
+ }
+ buildBluetoothDeviceList();
+ dispatchDeviceListAdded();
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ConnectionRecordManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ConnectionRecordManager.java
new file mode 100644
index 0000000..af343c4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ConnectionRecordManager.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * ConnectionRecordManager represents the sharedPreferences operation on device usage record
+ */
+public class ConnectionRecordManager {
+ private static final Object sInstanceSync = new Object();
+ private static final String KEY_LAST_SELECTED_DEVICE = "last_selected_device";
+ private static final String SHARED_PREFERENCES_NAME = "seamless_transfer_record";
+ private static final String TAG = "ConnectionRecordManager";
+ private static ConnectionRecordManager sInstance;
+
+ private String mLastSelectedDevice;
+
+ /**
+ * Get an {@code ConnectionRecordManager} instance (create one if necessary).
+ */
+ public static ConnectionRecordManager getInstance() {
+ synchronized (sInstanceSync) {
+ if (sInstance == null) {
+ sInstance = new ConnectionRecordManager();
+ }
+ }
+ return sInstance;
+ }
+
+ private SharedPreferences getSharedPreferences(Context context) {
+ return context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Get connection record from sharedPreferences
+ *
+ * @param id a unique device Id
+ * @return the the usage result
+ */
+ public synchronized int fetchConnectionRecord(Context context, String id) {
+ return getSharedPreferences(context).getInt(id, 0);
+ }
+
+ /**
+ * Get the last selected device from sharedPreferences
+ */
+ public synchronized void fetchLastSelectedDevice(Context context) {
+ mLastSelectedDevice = getSharedPreferences(context).getString(KEY_LAST_SELECTED_DEVICE,
+ null);
+ }
+
+ /**
+ * Set device usage time and last selected device in sharedPreference
+ *
+ * @param id a unique device Id
+ * @param record usage times
+ */
+ public synchronized void setConnectionRecord(Context context, String id, int record) {
+ final SharedPreferences.Editor editor = getSharedPreferences(context).edit();
+ // Update used times
+ mLastSelectedDevice = id;
+ editor.putInt(mLastSelectedDevice, record);
+ // Update last used device
+ editor.putString(KEY_LAST_SELECTED_DEVICE, mLastSelectedDevice);
+ editor.apply();
+ }
+
+ /**
+ * @return the last selected device
+ */
+ public synchronized String getLastSelectedDevice() {
+ return mLastSelectedDevice;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 498a0fc..a2b161f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,6 +16,7 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.widget.Toast;
import androidx.mediarouter.media.MediaRouter;
@@ -33,6 +34,7 @@
InfoMediaDevice(Context context, MediaRouter.RouteInfo info) {
super(context, MediaDeviceType.TYPE_CAST_DEVICE);
mRouteInfo = info;
+ initDeviceRecord();
}
@Override
@@ -52,14 +54,22 @@
}
@Override
+ public void notifyConnectedChanged() {
+ //TODO(b/117129183): check mIsConnected state
+ }
+
+ @Override
public void connect() {
//TODO(b/117129183): use MediaController2 to transfer media
mIsConnected = true;
+ super.connect();
+ //mIsConnected = true;
+ Toast.makeText(mContext, "This is cast device !", Toast.LENGTH_SHORT).show();
}
@Override
public void disconnect() {
//TODO(b/117129183): disconnected last select device
- mIsConnected = false;
+ //mIsConnected = false;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index e375ea0..154834e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -28,15 +28,20 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
/**
* LocalMediaManager provide interface to get MediaDevice list and transfer media to MediaDevice.
*/
public class LocalMediaManager implements BluetoothCallback {
-
+ private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
private static final String TAG = "LocalMediaManager";
+ public static final String NOTIFICATION_EXTRA = "notification_extra";
+ public static final String NOTIFICATION_PACKAGE_NAME = "notification_package_name";
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({MediaDeviceState.STATE_CONNECTED,
MediaDeviceState.STATE_CONNECTING,
@@ -56,7 +61,6 @@
private InfoMediaManager mInfoMediaManager;
private LocalBluetoothManager mLocalBluetoothManager;
- private MediaDevice mLastConnectedDevice;
private MediaDevice mPhoneDevice;
/**
@@ -96,29 +100,43 @@
* @param connectDevice the MediaDevice
*/
public void connectDevice(MediaDevice connectDevice) {
- if (connectDevice == mLastConnectedDevice) {
+ final MediaDevice currentDevice = getCurrentConnectedDevice();
+ final MediaDevice device =
+ MediaDeviceUtils.findMediaDevice(mMediaDevices, connectDevice.getId());
+ if (device != null && currentDevice != null
+ && device.getId().equals(currentDevice.getId())) {
return;
}
- if (mLastConnectedDevice != null) {
- mLastConnectedDevice.disconnect();
+ //TODO(b/117129183): For demo, will remove check connectDevice is InfoMediaDevice.
+ if (currentDevice != null && !(connectDevice instanceof InfoMediaDevice)) {
+ currentDevice.disconnect();
}
- connectDevice.connect();
- if (connectDevice.isConnected()) {
- mLastConnectedDevice = connectDevice;
- }
+ device.connect();
- final int state = connectDevice.isConnected()
+ final int state = device.isConnected()
? MediaDeviceState.STATE_CONNECTED
: MediaDeviceState.STATE_DISCONNECTED;
- dispatchSelectedDeviceStateChanged(connectDevice, state);
+ dispatchSelectedDeviceStateChanged(mMediaDevices, device, state);
}
- void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) {
+ private MediaDevice getCurrentConnectedDevice() {
+ for (MediaDevice device : mMediaDevices) {
+ if (device.isConnected()) {
+ return device;
+ }
+ }
+ Log.w(TAG, "getCurrentConnectedDevice() cannot find current connected device !");
+ return null;
+ }
+
+ void dispatchSelectedDeviceStateChanged(List<MediaDevice> mMediaDevices, MediaDevice device,
+ @MediaDeviceState int state) {
synchronized (mCallbacks) {
for (DeviceCallback callback : mCallbacks) {
- callback.onSelectedDeviceStateChanged(device, state);
+ callback.onSelectedDeviceStateChanged(new ArrayList<>(mMediaDevices), device,
+ state);
}
}
}
@@ -153,6 +171,7 @@
void dispatchDeviceListUpdate() {
synchronized (mCallbacks) {
+ Collections.sort(mMediaDevices, COMPARATOR);
for (DeviceCallback callback : mCallbacks) {
callback.onDeviceListUpdate(new ArrayList<>(mMediaDevices));
}
@@ -206,6 +225,25 @@
public void onDeviceAttributesChanged() {
dispatchDeviceListUpdate();
}
+
+ @Override
+ public void onActiveDeviceChanged(String id) {
+ final MediaDevice currentDevice = getCurrentConnectedDevice();
+ final MediaDevice connectDevice = MediaDeviceUtils.findMediaDevice(mMediaDevices, id);
+
+ if (connectDevice != null && currentDevice != null
+ && connectDevice.getId().equals(currentDevice.getId())) {
+ return;
+ }
+ if (currentDevice != null) {
+ currentDevice.notifyConnectedChanged();
+ }
+ if (connectDevice != null) {
+ connectDevice.notifyConnectedChanged();
+ }
+
+ dispatchDeviceListUpdate();
+ }
}
@@ -229,6 +267,7 @@
* {@link MediaDeviceState#STATE_CONNECTING},
* {@link MediaDeviceState#STATE_DISCONNECTED}
*/
- void onSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state);
+ void onSelectedDeviceStateChanged(List<MediaDevice> devices, MediaDevice device,
+ @MediaDeviceState int state);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 6c536f0..e4aeda6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,6 +16,8 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.IntDef;
@@ -25,20 +27,21 @@
/**
* MediaDevice represents a media device(such like Bluetooth device, cast device and phone device).
*/
-public abstract class MediaDevice {
-
+public abstract class MediaDevice implements Comparable<MediaDevice> {
private static final String TAG = "MediaDevice";
@Retention(RetentionPolicy.SOURCE)
- @IntDef({MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
- MediaDeviceType.TYPE_CAST_DEVICE,
+ @IntDef({MediaDeviceType.TYPE_CAST_DEVICE,
+ MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
MediaDeviceType.TYPE_PHONE_DEVICE})
public @interface MediaDeviceType {
- int TYPE_BLUETOOTH_DEVICE = 1;
- int TYPE_CAST_DEVICE = 2;
+ int TYPE_CAST_DEVICE = 1;
+ int TYPE_BLUETOOTH_DEVICE = 2;
int TYPE_PHONE_DEVICE = 3;
}
+ private int mConnectedRecord;
+
protected boolean mIsConnected = false;
protected Context mContext;
protected int mType;
@@ -48,12 +51,19 @@
mContext = context;
}
+ void initDeviceRecord() {
+ ConnectionRecordManager.getInstance().fetchLastSelectedDevice(mContext);
+ mConnectedRecord = ConnectionRecordManager.getInstance().fetchConnectionRecord(mContext,
+ getId());
+ Log.d("ttttt", getName() + " used: " + mConnectedRecord);
+ }
+
/**
* Check the MediaDevice is be connected to transfer.
*
* @return true if the MediaDevice is be connected to transfer, false otherwise.
*/
- protected boolean isConnected() {
+ public boolean isConnected() {
return mIsConnected;
}
@@ -78,12 +88,78 @@
public abstract String getId();
/**
+ * Notify MediaDevice to change their connected state.
+ */
+ public abstract void notifyConnectedChanged();
+
+ /**
* Transfer MediaDevice for media
*/
- public abstract void connect();
+ public void connect() {
+ mConnectedRecord++;
+ ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
+ mConnectedRecord);
+ }
/**
* Stop transfer MediaDevice
*/
public abstract void disconnect();
+
+ /**
+ * Rules:
+ * 1. If there is one of the connected devices identified as a carkit, this carkit will
+ * be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
+ * 2. For devices without any usage data yet
+ * WiFi device group sorted by alphabetical order + BT device group sorted by alphabetical
+ * order + phone speaker
+ * 3. For devices with usage record.
+ * The most recent used one + device group with usage info sorted by how many times the
+ * device has been used.
+ *
+ * So the device list will look like 4 slots ranked as below.
+ * Rule 1 + the most recently used device + Rule 3 + Rule 2
+ * Any slot could be empty. And available device will belong to one of the slots.
+ *
+ * @return a negative integer, zero, or a positive integer
+ * as this object is less than, equal to, or greater than the specified object.
+ */
+ @Override
+ public int compareTo(MediaDevice another) {
+ // Check carkit
+ if (isCarKitDevice()) {
+ return -1;
+ } else if (another.isCarKitDevice()) {
+ return 1;
+ }
+ // Set last used device at the first item
+ String lastSelectedDevice = ConnectionRecordManager.getInstance().getLastSelectedDevice();
+ if (TextUtils.equals(lastSelectedDevice, getId())) {
+ return -1;
+ } else if (TextUtils.equals(lastSelectedDevice, another.getId())) {
+ return 1;
+ }
+ // Sort by how many times the device has been used if there is usage record
+ if ((mConnectedRecord != another.mConnectedRecord)
+ && (another.mConnectedRecord > 0 || mConnectedRecord > 0)) {
+ return (another.mConnectedRecord - mConnectedRecord);
+ }
+ // Both devices have never been used
+ // To devices with the same type, sort by alphabetical order
+ if (mType == another.mType) {
+ final String s1 = getName();
+ final String s2 = another.getName();
+ return s1.compareToIgnoreCase(s2);
+ }
+ // Both devices have never been used, the priority is Cast > Bluetooth > Phone
+ return mType - another.mType;
+ }
+
+ /**
+ * Check if it is CarKit device
+ * @return true if it is CarKit device
+ */
+ protected boolean isCarKitDevice() {
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
index 060e9ad..8066eb0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
@@ -15,15 +15,21 @@
*/
package com.android.settingslib.media;
+import android.util.Log;
+
import androidx.mediarouter.media.MediaRouter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import java.util.List;
+
/**
* MediaDeviceUtils provides utility function for MediaDevice
*/
public class MediaDeviceUtils {
+ private static final String TAG = "MediaDeviceUtils";
+
/**
* Use CachedBluetoothDevice address to represent unique id
*
@@ -43,4 +49,21 @@
public static String getId(MediaRouter.RouteInfo route) {
return route.getId();
}
+
+ /**
+ * Find the MediaDevice through id.
+ *
+ * @param devices the list of MediaDevice
+ * @param id the unique id of MediaDevice
+ * @return MediaDevice
+ */
+ public static MediaDevice findMediaDevice(List<MediaDevice> devices, String id) {
+ for (MediaDevice mediaDevice : devices) {
+ if (mediaDevice.getId().equals(id)) {
+ return mediaDevice;
+ }
+ }
+ Log.e(TAG, "findMediaDevice() can't found device");
+ return null;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
index 72b6b09..ee11070 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
@@ -17,7 +17,6 @@
import android.app.Notification;
import android.content.Context;
-import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
@@ -68,13 +67,7 @@
public abstract void stopScan();
protected MediaDevice findMediaDevice(String id) {
- for (MediaDevice mediaDevice : mMediaDevices) {
- if (mediaDevice.getId().equals(id)) {
- return mediaDevice;
- }
- }
- Log.e(TAG, "findMediaDevice() can't found device");
- return null;
+ return MediaDeviceUtils.findMediaDevice(mMediaDevices, id);
}
protected void dispatchDeviceAdded(MediaDevice mediaDevice) {
@@ -96,7 +89,7 @@
protected void dispatchDeviceListAdded() {
synchronized (mCallbacks) {
for (MediaDeviceCallback callback : mCallbacks) {
- callback.onDeviceListAdded(mMediaDevices);
+ callback.onDeviceListAdded(new ArrayList<>(mMediaDevices));
}
}
}
@@ -117,6 +110,14 @@
}
}
+ protected void dispatchActiveDeviceChanged(String id) {
+ synchronized (mCallbacks) {
+ for (MediaDeviceCallback callback : mCallbacks) {
+ callback.onActiveDeviceChanged(id);
+ }
+ }
+ }
+
/**
* Callback for notifying device is added, removed and attributes changed.
*/
@@ -153,5 +154,12 @@
* Callback for notifying MediaDevice attributes is changed.
*/
void onDeviceAttributesChanged();
+
+ /**
+ * Callback for notifying active MediaDevice is changed.
+ *
+ * @param id the id of MediaDevice
+ */
+ void onActiveDeviceChanged(String id);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 5e49d6b..59150f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -15,6 +15,7 @@
*/
package com.android.settingslib.media;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.util.Log;
@@ -24,6 +25,8 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import java.util.List;
+
/**
* PhoneMediaDevice extends MediaDevice to represents Phone device.
*/
@@ -41,6 +44,30 @@
mLocalBluetoothManager = localBluetoothManager;
mProfileManager = mLocalBluetoothManager.getProfileManager();
+ initDeviceRecord();
+
+ mIsConnected = isPhoneActive();
+ }
+
+ private boolean isPhoneActive() {
+ boolean isActive = true;
+
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+
+ if (a2dpProfile.getActiveDevice() == null) {
+ final List<BluetoothDevice> activeDevices = hapProfile.getActiveDevices();
+ for (BluetoothDevice btDevice : activeDevices) {
+ if (btDevice != null) {
+ isActive = false;
+ break;
+ }
+ }
+ } else {
+ isActive = false;
+ }
+
+ return isActive;
}
@Override
@@ -61,13 +88,18 @@
}
@Override
+ public void notifyConnectedChanged() {
+ mIsConnected = isPhoneActive();
+ }
+
+ @Override
public void connect() {
final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
if (hapProfile != null && a2dpProfile != null) {
- mIsConnected =
- hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
+ mIsConnected = hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
+ super.connect();
}
Log.d(TAG, "connect() device : " + getName() + ", is selected : " + mIsConnected);
}
diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk
index c893b6d..4a814df 100644
--- a/packages/SettingsLib/tests/integ/Android.mk
+++ b/packages/SettingsLib/tests/integ/Android.mk
@@ -31,8 +31,8 @@
LOCAL_USE_AAPT2 := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
- espresso-core \
+ androidx.test.rules \
+ androidx.test.espresso.core \
mockito-target-minus-junit4 \
truth-prebuilt
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index e8e0b41..da808dd 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -31,7 +31,7 @@
<activity android:name=".drawer.SettingsDrawerActivityTest$TestActivity"/>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.settingslib"
android:label="Tests for SettingsLib">
</instrumentation>
diff --git a/packages/SettingsLib/tests/integ/AndroidTest.xml b/packages/SettingsLib/tests/integ/AndroidTest.xml
index d7ee618..b5d0947 100644
--- a/packages/SettingsLib/tests/integ/AndroidTest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="SettingsLibTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.settingslib" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
index d0ab46a..50f5b9d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
@@ -16,15 +16,10 @@
package com.android.settingslib.bluetooth;
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -32,15 +27,18 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
import java.util.concurrent.CountDownLatch;
/**
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
index 3fa2ce5..a436cb5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
@@ -27,8 +27,9 @@
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index dddfa7a..08484bc 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -1,19 +1,7 @@
package com.android.settingslib.graph;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import com.android.settingslib.R;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyString;
@@ -21,6 +9,21 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryMeterDrawableBaseTest {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
index 93b038e..9962e1c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
@@ -20,12 +20,13 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
index e591d8c..f1c0beae 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
@@ -16,11 +16,12 @@
package com.android.settingslib.inputmethod;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
index 54510b2..46557d3 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -33,8 +32,9 @@
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
index ee03d50..37f2600 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
@@ -16,19 +16,22 @@
package com.android.settingslib.utils;
+import static junit.framework.Assert.assertEquals;
+
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.settingslib.NetworkPolicyEditor;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static junit.framework.Assert.assertEquals;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkPolicyEditorTest {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
index 0ec75ec..ff8dbda 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java
@@ -15,21 +15,23 @@
*/
package com.android.settingslib.utils;
+import static junit.framework.Assert.assertTrue;
+
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.filters.SmallTest;
import android.text.Spanned;
import android.text.style.TtsSpan;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.settingslib.datetime.ZoneGetter;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.*;
-import com.android.settingslib.datetime.ZoneGetter;
-
-import static junit.framework.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 03247999..fc3034e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -22,7 +22,6 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,12 +43,12 @@
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableString;
import android.text.format.DateUtils;
-import android.text.style.TtsSpan;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.R;
import com.android.settingslib.utils.ThreadUtils;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 1860b31..42eb0b9 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -53,9 +53,10 @@
import android.os.HandlerThread;
import android.os.SystemClock;
import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.utils.ThreadUtils;
@@ -79,7 +80,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
// TODO(sghuman): Change these to robolectric tests b/35766684.
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WifiTrackerTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 27b8dfc..0dcdaed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -18,6 +18,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -154,4 +155,30 @@
verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED);
}
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
+ when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_DISCONNECTED);
+ }
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
+ when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_CONNECTED);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 47b1210..43b2894 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -334,6 +335,27 @@
}
/**
+ * Test to verify isSubDevice_validSubDevice().
+ */
+ @Test
+ public void isSubDevice_validSubDevice() {
+ doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+ mCachedDeviceManager.addDevice(mDevice1);
+
+ // Both device are not sub device in default value.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
+
+ // Add Device-2 as sub device of Device-1 with same HiSyncId.
+ doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+ mCachedDeviceManager.addDevice(mDevice2);
+
+ // Verify Device-2 is sub device, but Device-1 is not.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ }
+
+ /**
* Test to verify updateHearingAidsDevices().
*/
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 28de191..f695e0c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -35,6 +35,8 @@
@RunWith(RobolectricTestRunner.class)
public class AbstractPreferenceControllerTest {
+ private static final String KEY_PREF = "test_pref";
+
@Mock
private PreferenceScreen mScreen;
@@ -47,9 +49,9 @@
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
- mPreference.setKey(TestPrefController.KEY_PREF);
- when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
- mTestPrefController = new TestPrefController(mContext);
+ mPreference.setKey(KEY_PREF);
+ when(mScreen.findPreference(KEY_PREF)).thenReturn(mPreference);
+ mTestPrefController = new TestPrefController(mContext, KEY_PREF);
}
@Test
@@ -62,15 +64,24 @@
}
@Test
+ public void displayPref_noKey_shouldDoNothing() {
+ mTestPrefController.isAvailable = true;
+
+ mTestPrefController.displayPreference(mScreen);
+
+ assertThat(mPreference.isVisible()).isTrue();
+ }
+
+ @Test
public void setVisible_prefIsVisible_shouldSetToVisible() {
- mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, true /* visible */);
+ mTestPrefController.setVisible(mScreen, KEY_PREF, true /* visible */);
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void setVisible_prefNotVisible_shouldSetToInvisible() {
- mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, false /* visible */);
+ mTestPrefController.setVisible(mScreen, KEY_PREF, false /* visible */);
assertThat(mPreference.isVisible()).isFalse();
}
@@ -92,13 +103,14 @@
}
private static class TestPrefController extends AbstractPreferenceController {
- private static final String KEY_PREF = "test_pref";
private static final CharSequence TEST_SUMMARY = "Test";
public boolean isAvailable;
+ private final String mPrefKey;
- public TestPrefController(Context context) {
+ TestPrefController(Context context, String key) {
super(context);
+ mPrefKey = key;
}
@Override
@@ -113,7 +125,7 @@
@Override
public String getPreferenceKey() {
- return KEY_PREF;
+ return mPrefKey;
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
new file mode 100644
index 0000000..1cbf277
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.mediarouter.media.MediaRouter;
+
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+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.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class MediaDeviceTest {
+ private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
+ private static final String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+ private static final String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+ private static final String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
+ private static final String DEVICE_NAME_1 = "TestName_1";
+ private static final String DEVICE_NAME_2 = "TestName_2";
+ private static final String DEVICE_NAME_3 = "TestName_3";
+ private static final String ROUTER_ID_1 = "RouterId_1";
+ private static final String ROUTER_ID_2 = "RouterId_2";
+ private static final String ROUTER_ID_3 = "RouterId_3";
+ private final BluetoothClass mHeadreeClass =
+ new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+ private final BluetoothClass mCarkitClass =
+ new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
+
+ @Mock
+ private A2dpProfile mA2dpProfile;
+ @Mock
+ private BluetoothDevice mDevice1;
+ @Mock
+ private BluetoothDevice mDevice2;
+ @Mock
+ private BluetoothDevice mDevice3;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice1;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice2;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice3;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private MediaRouter.RouteInfo mRouteInfo1;
+ @Mock
+ private MediaRouter.RouteInfo mRouteInfo2;
+ @Mock
+ private MediaRouter.RouteInfo mRouteInfo3;
+
+ private BluetoothMediaDevice mBluetoothMediaDevice1;
+ private BluetoothMediaDevice mBluetoothMediaDevice2;
+ private BluetoothMediaDevice mBluetoothMediaDevice3;
+ private Context mContext;
+ private InfoMediaDevice mInfoMediaDevice1;
+ private InfoMediaDevice mInfoMediaDevice2;
+ private InfoMediaDevice mInfoMediaDevice3;
+ private List<MediaDevice> mMediaDevices = new ArrayList<>();
+ private PhoneMediaDevice mPhoneMediaDevice;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ when(mCachedDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+ when(mCachedDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+ when(mCachedDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
+ when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME_1);
+ when(mCachedDevice2.getName()).thenReturn(DEVICE_NAME_2);
+ when(mCachedDevice3.getName()).thenReturn(DEVICE_NAME_3);
+ when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
+ when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
+ when(mCachedDevice3.getDevice()).thenReturn(mDevice3);
+ when(mRouteInfo1.getId()).thenReturn(ROUTER_ID_1);
+ when(mRouteInfo2.getId()).thenReturn(ROUTER_ID_2);
+ when(mRouteInfo3.getId()).thenReturn(ROUTER_ID_3);
+ when(mRouteInfo1.getName()).thenReturn(DEVICE_NAME_1);
+ when(mRouteInfo2.getName()).thenReturn(DEVICE_NAME_2);
+ when(mRouteInfo3.getName()).thenReturn(DEVICE_NAME_3);
+ when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
+ when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice1);
+
+ mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
+ mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
+ mBluetoothMediaDevice3 = new BluetoothMediaDevice(mContext, mCachedDevice3);
+ mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
+ mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
+ mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
+ mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
+ }
+
+ @Test
+ public void compareTo_carKit_nonCarKitBluetooth_carKitFirst() {
+ when(mDevice1.getBluetoothClass()).thenReturn(mHeadreeClass);
+ when(mDevice2.getBluetoothClass()).thenReturn(mCarkitClass);
+ mMediaDevices.add(mBluetoothMediaDevice1);
+ mMediaDevices.add(mBluetoothMediaDevice2);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+ }
+
+ @Test
+ public void compareTo_carKit_info_carKitFirst() {
+ when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
+ mMediaDevices.add(mInfoMediaDevice1);
+ mMediaDevices.add(mBluetoothMediaDevice1);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ }
+
+ @Test
+ public void compareTo_carKit_phone_carKitFirst() {
+ when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
+ mMediaDevices.add(mPhoneMediaDevice);
+ mMediaDevices.add(mBluetoothMediaDevice1);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ }
+
+ @Test
+ public void compareTo_lastSelected_others_lastSelectedFirst() {
+ mMediaDevices.add(mBluetoothMediaDevice1);
+ mMediaDevices.add(mBluetoothMediaDevice2);
+ mBluetoothMediaDevice2.connect();
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+ }
+ @Test
+ public void compareTo_connectionRecord_sortByRecord() {
+ mMediaDevices.add(mBluetoothMediaDevice1);
+ mMediaDevices.add(mBluetoothMediaDevice2);
+ mBluetoothMediaDevice1.connect();
+ mBluetoothMediaDevice2.connect();
+ mBluetoothMediaDevice2.connect();
+ // Reset last selected record
+ ConnectionRecordManager.getInstance().setConnectionRecord(mContext, null, 0);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice2);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+ assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice1);
+ }
+
+ @Test
+ public void compareTo_info_bluetooth_infoFirst() {
+ mMediaDevices.add(mBluetoothMediaDevice1);
+ mMediaDevices.add(mInfoMediaDevice1);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
+ }
+
+ @Test
+ public void compareTo_bluetooth_phone_bluetoothFirst() {
+ mMediaDevices.add(mPhoneMediaDevice);
+ mMediaDevices.add(mBluetoothMediaDevice1);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ }
+
+ @Test
+ public void compareTo_twoInfo_sortByAlphabet() {
+ mMediaDevices.add(mInfoMediaDevice2);
+ mMediaDevices.add(mInfoMediaDevice1);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice2);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
+ }
+
+ @Test
+ public void compareTo_twoBluetooth_sortByAlphabet() {
+ mMediaDevices.add(mBluetoothMediaDevice2);
+ mMediaDevices.add(mBluetoothMediaDevice1);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ }
+
+ // 1.mInfoMediaDevice1: Last Selected device
+ // 2.mBluetoothMediaDevice1: CarKit device
+ // 3.mInfoMediaDevice2: * 2 times usage
+ // 4.mInfoMediaDevice3: * 1 time usage
+ // 5.mBluetoothMediaDevice2: * 2 times usage
+ // 6.mBluetoothMediaDevice3: * 1 time usage
+ // 7.mPhoneMediaDevice: * 0 time usage
+ // Order: 2 -> 1 -> 3 -> 5 -> 4 -> 6 -> 7
+ @Test
+ public void compareTo_mixedDevices_carKitFirst() {
+ when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
+ when(mDevice2.getBluetoothClass()).thenReturn(mHeadreeClass);
+ when(mDevice3.getBluetoothClass()).thenReturn(mHeadreeClass);
+ mMediaDevices.add(mBluetoothMediaDevice1);
+ mMediaDevices.add(mBluetoothMediaDevice2);
+ mMediaDevices.add(mBluetoothMediaDevice3);
+ mMediaDevices.add(mInfoMediaDevice1);
+ mMediaDevices.add(mInfoMediaDevice2);
+ mMediaDevices.add(mInfoMediaDevice3);
+ mMediaDevices.add(mPhoneMediaDevice);
+ mBluetoothMediaDevice3.connect();
+ mBluetoothMediaDevice2.connect();
+ mBluetoothMediaDevice2.connect();
+ mInfoMediaDevice3.connect();
+ mInfoMediaDevice2.connect();
+ mInfoMediaDevice2.connect();
+ mInfoMediaDevice1.connect();
+
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ assertThat(mMediaDevices.get(1)).isEqualTo(mInfoMediaDevice1);
+ assertThat(mMediaDevices.get(2)).isEqualTo(mInfoMediaDevice2);
+ assertThat(mMediaDevices.get(3)).isEqualTo(mBluetoothMediaDevice2);
+ assertThat(mMediaDevices.get(4)).isEqualTo(mInfoMediaDevice3);
+ assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice3);
+ assertThat(mMediaDevices.get(6)).isEqualTo(mPhoneMediaDevice);
+ }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
index 371c3d4..375b45c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -41,6 +41,7 @@
private BarView mBarView2;
private BarView mBarView3;
private BarView mBarView4;
+ private TextView mDetailsView;
private PreferenceViewHolder mHolder;
private BarChartPreference mPreference;
@@ -51,14 +52,13 @@
mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView);
mPreference = new BarChartPreference(mContext, null /* attrs */);
mPreference.setBarChartTitle(R.string.debug_app);
- mPreference.setBarChartDetails(R.string.debug_app);
-
mIcon = mContext.getDrawable(R.drawable.ic_menu);
mBarView1 = (BarView) mBarChartView.findViewById(R.id.bar_view1);
mBarView2 = (BarView) mBarChartView.findViewById(R.id.bar_view2);
mBarView3 = (BarView) mBarChartView.findViewById(R.id.bar_view3);
mBarView4 = (BarView) mBarChartView.findViewById(R.id.bar_view4);
+ mDetailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
}
@Test
@@ -73,26 +73,31 @@
}
@Test
- public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
- final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+ public void onBindViewHolder_notSetDetailsRes_barChartDetailsViewIsGone() {
+ // We don't call BarChartPreference#setBarChartDetails
+ mPreference.onBindViewHolder(mHolder);
+ assertThat(mDetailsView.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
mPreference.setBarChartDetails(R.string.debug_app);
mPreference.onBindViewHolder(mHolder);
- assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(detailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mDetailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
}
@Test
public void setBarChartDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
- final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
-
+ mPreference.setBarChartDetails(R.string.debug_app);
mPreference.setBarChartDetailsClickListener(v -> {
});
mPreference.onBindViewHolder(mHolder);
- assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(detailsView.hasOnClickListeners()).isTrue();
+ assertThat(mDetailsView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mDetailsView.hasOnClickListeners()).isTrue();
}
@Test
@@ -208,4 +213,18 @@
assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app));
}
+
+ @Test
+ public void setAllBarViewsInfo_setClickListenerForBarView_barViewAttachClickListener() {
+ final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
+ viewInfo.setClickListener(v -> {
+ });
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.hasOnClickListeners()).isTrue();
+ }
}
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index 8e8d9f7..abe82a8 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -18,7 +18,7 @@
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.os.SystemProperties;
+import android.sysprop.SetupWizardProperties;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
@@ -51,7 +51,7 @@
// Setup theme for aosp/pixel
setTheme(
WizardManagerHelper.getThemeRes(
- SystemProperties.get("setupwizard.theme"),
+ SetupWizardProperties.theme().orElse(""),
R.style.SuwThemeGlif_Light
)
);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ad44b9a..8be67d9 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -115,6 +115,8 @@
"mockito-target-inline-minus-junit4",
"testables",
"truth-prebuilt",
+ "dagger2-2.19",
+ "jsr330"
],
libs: [
"android.test.runner",
@@ -125,6 +127,7 @@
"--extra-packages",
"com.android.keyguard:com.android.systemui",
],
+ annotation_processors: ["dagger2-compiler-2.19"],
}
android_app {
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
new file mode 100644
index 0000000..7751bb1
--- /dev/null
+++ b/packages/SystemUI/docs/dagger.md
@@ -0,0 +1,200 @@
+# Dagger 2 in SystemUI
+*Dagger 2 is a dependency injection framework that compiles annotations to code
+to create dependencies without reflection*
+
+## Recommended reading
+
+Go read about Dagger 2.
+
+TODO: Add some links.
+
+## State of the world
+
+Dagger 2 has been turned on for SystemUI and a early first pass has been taken
+for converting everything in Dependency.java to use Dagger. Since a lot of
+SystemUI depends on Dependency, stubs have been added to Dependency to proxy
+any gets through to the instances provided by dagger, this will allow migration
+of SystemUI through a number of CLs.
+
+### How it works in SystemUI
+
+For the classes that we're using in Dependency and are switching to dagger, the
+equivalent dagger version is using @Singleton and only having one instance.
+To have the single instance span all of SystemUI and be easily accessible for
+other components, there is a single root Component that exists that generates
+these. The component lives in SystemUIFactory and is called SystemUIRootComponent.
+
+```java
+@Singleton
+@Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+public interface SystemUIRootComponent {
+ @Singleton
+ Dependency.DependencyInjector createDependency();
+}
+```
+
+The root modules are what provides the global singleton dependencies across
+SystemUI. ContextHolder is just a wrapper that provides a context.
+SystemUIFactory @Provide dependencies that need to be overridden by SystemUI
+variants (like other form factors). DependencyBinder creates the mapping from
+interfaces to implementation classes. DependencyProvider provides or binds any
+remaining depedencies required.
+
+### Adding injection to a new SystemUI object
+
+Anything that depends on any @Singleton provider from SystemUIRootComponent
+should be declared as a Subcomponent of the root component, this requires
+declaring your own interface for generating your own modules or just the
+object you need injected. The subcomponent also needs to be added to
+SystemUIRootComponent in SystemUIFactory so it can be acquired.
+
+```java
+public interface SystemUIRootComponent {
++ @Singleton
++ Dependency.DependencyInjector createDependency();
+}
+
+public class Dependency extends SystemUI {
+ ...
++ @Subcomponent
++ public interface DependencyInjector {
++ Dependency createSystemUI();
++ }
+}
+```
+
+For objects that extend SystemUI and require injection, you can define an
+injector that creates the injected object for you. This other class should
+be referenced in @string/config_systemUIServiceComponents.
+
+```java
+public static class DependencyCreator implements Injector {
+ @Override
+ public SystemUI apply(Context context) {
+ return SystemUIFactory.getInstance().getRootComponent()
+ .createDependency()
+ .createSystemUI();
+ }
+}
+```
+
+### Adding a new injectable object
+
+First tag the constructor with @Inject. Also tag it with @Singleton if only one
+instance should be created.
+
+```java
+@Singleton
+public class SomethingController {
+ @Inject
+ public SomethingController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ // context and mainHandler will be automatically populated.
+ }
+}
+```
+
+If you have an interface class and an implementation class, dagger needs to know
+how to map it. The simplest way to do this is to add a provides method to
+DependencyProvider.
+
+```java
+public class DependencyProvider {
+ ...
+ @Singleton
+ @Provide
+ public SomethingController provideSomethingController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ return new SomethingControllerImpl(context, mainHandler);
+ }
+}
+```
+
+If you need to access this from Dependency#get, then add an adapter to Dependency
+that maps to the instance provided by Dagger. The changes should be similar
+to the following diff.
+
+```java
+public class Dependency {
+ ...
+ @Inject Lazy<SomethingController> mSomethingController;
+ ...
+ public void start() {
+ ...
+ mProviders.put(SomethingController.class, mSomethingController::get);
+ }
+}
+```
+
+### Using injection with Fragments
+
+Fragments are created as part of the FragmentManager, so they need to be
+setup so the manager knows how to create them. To do that, add a method
+to com.android.systemui.fragments.FragmentService$FragmentCreator that
+returns your fragment class. Thats all thats required, once the method
+exists, FragmentService will automatically pick it up and use injection
+whenever your fragment needs to be created.
+
+```java
+public interface FragmentCreator {
++ NavigationBarFragment createNavigationBar();
+}
+```
+
+If you need to create your fragment (i.e. for the add or replace transaction),
+then the FragmentHostManager can do this for you.
+
+```java
+FragmentHostManager.get(view).create(NavigationBarFragment.class);
+```
+
+### Using injection with Views
+
+Generally, you shouldn't need to inject for a view, as the view should
+be relatively self contained and logic that requires injection should be
+moved to a higher level construct such as a Fragment or a top-level SystemUI
+component, see above for how to do injection for both of which.
+
+Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
+lot of code that could benefit from injection and will need to be migrated
+off from Dependency#get uses. Similar to how fragments are injected, the view
+needs to be added to the interface
+com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+
+```java
+public interface ViewInstanceCreator {
++ QuickStatusBarHeader createQsHeader();
+}
+```
+
+Presumably you need to inflate that view from XML (otherwise why do you
+need anything special? see earlier sections about generic injection). To obtain
+an inflater that supports injected objects, call InjectionInflationController#injectable,
+which will wrap the inflater it is passed in one that can create injected
+objects when needed.
+
+```java
+@Override
+public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+ return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
+}
+```
+
+There is one other important thing to note about injecting with views. SysUI
+already has a Context in its global dagger component, so if you simply inject
+a Context, you will not get the one that the view should have with proper
+theming. Because of this, always ensure to tag views that have @Inject with
+the @Named view context.
+
+```java
+public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
+ OtherCustomDependency something) {
+ ...
+}
+```
+
+## TODO List
+
+ - Eliminate usages of Dependency#get
+ - Add links in above TODO
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 6135aeb..ac69043 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -36,6 +36,13 @@
View getView();
/**
+ * Get clock view for a large clock that appears behind NSSL.
+ */
+ default View getBigClockView() {
+ return null;
+ }
+
+ /**
* Set clock paint style.
* @param style The new style to set in the paint.
*/
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index ee94aed..22b0ab7 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -17,6 +17,7 @@
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
-keep class * extends com.android.systemui.SystemUI
+-keep class * implements com.android.systemui.SystemUI$Injector
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
@@ -27,4 +28,10 @@
-keep class com.android.systemui.plugins.** {
*;
}
+-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
+ *;
+}
+-keep class com.android.systemui.util.InjectionInflationController$ViewInstanceCreator {
+ *;
+}
-keep class androidx.core.app.CoreComponentFactory
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index ffc7b3c..b9966cf 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -33,7 +33,7 @@
<item name="android:gravity">center_horizontal|center_vertical</item>
<item name="android:background">@null</item>
<item name="android:textSize">32sp</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textColor">?attr/wallpaperTextColor</item>
<item name="android:paddingBottom">-16dp</item>
</style>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index ed18dc7..4e0cbe0 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -24,13 +24,14 @@
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:background="@android:color/transparent"
android:baselineAligned="false"
- android:clickable="false"
+ android:clickable="true"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
android:paddingStart="0dp"
- android:elevation="4dp" >
+ android:elevation="4dp"
+ android:importantForAccessibility="no" >
<include layout="@layout/quick_status_bar_header_system_icons" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2674f07c..75c0ec3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -25,6 +25,12 @@
android:layout_height="match_parent"
android:background="@android:color/transparent" >
+ <FrameLayout
+ android:id="@+id/big_clock_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" />
+
<include
layout="@layout/keyguard_status_view"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 61efbd5..889db66 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -290,7 +290,7 @@
<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
- <item>com.android.systemui.Dependency</item>
+ <item>com.android.systemui.Dependency$DependencyCreator</item>
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
@@ -318,7 +318,7 @@
<!-- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents -->
<string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
- <item>com.android.systemui.Dependency</item>
+ <item>com.android.systemui.Dependency$DependencyCreator</item>
<item>com.android.systemui.util.NotificationChannels</item>
</string-array>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 07375ad..f384d8f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2330,4 +2330,10 @@
</plurals>
<!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
<string name="sensor_privacy_mode">Sensors off</string>
+
+ <!-- Name for device services grouping system uid apps in Ongoing Privacy Dialog [CHAR_LIMIT=NONE] -->
+ <string name="device_services">Device Services</string>
+
+ <!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
+ <string name="music_controls_no_title">No title</string>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 10b5f1c..da143f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -258,7 +258,8 @@
Log.v(TAG, "Reloading " + pkg);
}
}
- if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
+ if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())
+ && componentName != null) {
@PluginEnabler.DisableReason int disableReason =
getPluginEnabler().getDisableReason(componentName);
if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
new file mode 100644
index 0000000..c0a1d89
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -0,0 +1,103 @@
+/*
+ * 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.shared.system;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.ThreadedRenderer;
+import android.view.View;
+import android.view.ViewRootImpl;
+
+import java.util.function.Consumer;
+
+/**
+ * Helper class to apply surface transactions in sync with RenderThread.
+ */
+public class SyncRtSurfaceTransactionApplierCompat {
+
+ private final SyncRtSurfaceTransactionApplier mApplier;
+
+ /**
+ * @param targetView The view in the surface that acts as synchronization anchor.
+ */
+ public SyncRtSurfaceTransactionApplierCompat(View targetView) {
+ mApplier = new SyncRtSurfaceTransactionApplier(targetView);
+ }
+
+ private SyncRtSurfaceTransactionApplierCompat(SyncRtSurfaceTransactionApplier applier) {
+ mApplier = applier;
+ }
+
+ /**
+ * Schedules applying surface parameters on the next frame.
+ *
+ * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
+ * this method to avoid synchronization issues.
+ */
+ public void scheduleApply(final SurfaceParams... params) {
+ mApplier.scheduleApply(convert(params));
+ }
+
+ private SyncRtSurfaceTransactionApplier.SurfaceParams[] convert(SurfaceParams[] params) {
+ SyncRtSurfaceTransactionApplier.SurfaceParams[] result =
+ new SyncRtSurfaceTransactionApplier.SurfaceParams[params.length];
+ for (int i = 0; i < params.length; i++) {
+ result[i] = params[i].mParams;
+ }
+ return result;
+ }
+
+ public static void applyParams(TransactionCompat t, SurfaceParams params) {
+ SyncRtSurfaceTransactionApplier.applyParams(t.mTransaction, params.mParams, t.mTmpValues);
+ }
+
+ public static void create(final View targetView,
+ final Consumer<SyncRtSurfaceTransactionApplierCompat> callback) {
+ SyncRtSurfaceTransactionApplier.create(targetView,
+ new Consumer<SyncRtSurfaceTransactionApplier>() {
+ @Override
+ public void accept(SyncRtSurfaceTransactionApplier applier) {
+ callback.accept(new SyncRtSurfaceTransactionApplierCompat(applier));
+ }
+ });
+ }
+
+ public static class SurfaceParams {
+
+ private final SyncRtSurfaceTransactionApplier.SurfaceParams mParams;
+
+ /**
+ * Constructs surface parameters to be applied when the current view state gets pushed to
+ * RenderThread.
+ *
+ * @param surface The surface to modify.
+ * @param alpha Alpha to apply.
+ * @param matrix Matrix to apply.
+ * @param windowCrop Crop to apply.
+ */
+ public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
+ Rect windowCrop, int layer, float cornerRadius) {
+ mParams = new SyncRtSurfaceTransactionApplier.SurfaceParams(surface.mSurfaceControl,
+ alpha, matrix, windowCrop, layer, cornerRadius);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 22a23a8..570d351 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -35,7 +35,11 @@
/**
* Frame for default and custom clock.
*/
- private FrameLayout mClockFrame;
+ private FrameLayout mSmallClockFrame;
+ /**
+ * Container for big custom clock.
+ */
+ private ViewGroup mBigClockContainer;
/**
* Status area (date and other stuff) shown below the clock. Plugin can decide whether
* or not to show it below the alternate clock.
@@ -46,22 +50,27 @@
new PluginListener<ClockPlugin>() {
@Override
public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
- View view = plugin.getView();
- if (view != null) {
- disconnectPlugin();
+ disconnectPlugin();
+ View smallClockView = plugin.getView();
+ if (smallClockView != null) {
// For now, assume that the most recently connected plugin is the
// selected clock face. In the future, the user should be able to
// pick a clock face from the available plugins.
- mClockPlugin = plugin;
- mClockFrame.addView(view, -1,
+ mSmallClockFrame.addView(smallClockView, -1,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
initPluginParams();
mClockView.setVisibility(View.GONE);
- if (!plugin.shouldShowStatusArea()) {
- mKeyguardStatusArea.setVisibility(View.GONE);
- }
}
+ View bigClockView = plugin.getBigClockView();
+ if (bigClockView != null && mBigClockContainer != null) {
+ mBigClockContainer.addView(bigClockView);
+ mBigClockContainer.setVisibility(View.VISIBLE);
+ }
+ if (!plugin.shouldShowStatusArea()) {
+ mKeyguardStatusArea.setVisibility(View.GONE);
+ }
+ mClockPlugin = plugin;
}
@Override
@@ -86,7 +95,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mClockView = findViewById(R.id.default_clock_view);
- mClockFrame = findViewById(R.id.clock_view);
+ mSmallClockFrame = findViewById(R.id.clock_view);
mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
}
@@ -104,6 +113,20 @@
}
/**
+ * Set container for big clock face appearing behind NSSL and KeyguardStatusView.
+ */
+ public void setBigClockContainer(ViewGroup container) {
+ if (mClockPlugin != null && container != null) {
+ View bigClockView = mClockPlugin.getBigClockView();
+ if (bigClockView != null) {
+ container.addView(bigClockView);
+ container.setVisibility(View.VISIBLE);
+ }
+ }
+ mBigClockContainer = container;
+ }
+
+ /**
* It will also update plugin setStyle if plugin is connected.
*/
public void setStyle(Style style) {
@@ -199,9 +222,13 @@
private void disconnectPlugin() {
if (mClockPlugin != null) {
- View view = mClockPlugin.getView();
- if (view != null) {
- mClockFrame.removeView(view);
+ View smallClockView = mClockPlugin.getView();
+ if (smallClockView != null) {
+ mSmallClockFrame.removeView(smallClockView);
+ }
+ if (mBigClockContainer != null) {
+ mBigClockContainer.removeAllViews();
+ mBigClockContainer.setVisibility(View.GONE);
}
mClockPlugin = null;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index e051317..583dac7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -28,6 +28,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.View;
import android.view.WindowManager;
@@ -45,6 +46,7 @@
private final Context mContext;
private boolean mShowing;
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final SparseArray<Presentation> mPresentations = new SparseArray<>();
@@ -86,6 +88,22 @@
mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
}
+ private boolean isKeyguardShowable(Display display) {
+ if (display == null) {
+ if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
+ return false;
+ }
+ if (display.getDisplayId() == DEFAULT_DISPLAY) {
+ if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
+ return false;
+ }
+ display.getDisplayInfo(mTmpDisplayInfo);
+ if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
+ if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
+ return false;
+ }
+ return true;
+ }
/**
* @param display The display to show the presentation on.
* @return {@code true} if a presentation was added.
@@ -93,7 +111,7 @@
* was already there.
*/
private boolean showPresentation(Display display) {
- if (display == null || display.getDisplayId() == DEFAULT_DISPLAY) return false;
+ if (!isKeyguardShowable(display)) return false;
if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
final int displayId = display.getDisplayId();
Presentation presentation = mPresentations.get(displayId);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 6528d8c..c0ec405 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -66,17 +66,6 @@
public void onSimStateChanged(int subId, int slotId, State simState) {
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
switch(simState) {
- // If the SIM is removed, then we must remove the keyguard. It will be put up
- // again when the PUK locked SIM is re-entered.
- case ABSENT: {
- KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
- // onSimStateChanged callback can fire when the SIM PIN lock is not currently
- // active and mCallback is null.
- if (mCallback != null) {
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- }
- break;
- }
case READY: {
mRemainingAttempts = -1;
resetState();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 1157f86..b17d117 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -69,14 +69,11 @@
public void onSimStateChanged(int subId, int slotId, State simState) {
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
switch(simState) {
- // If the SIM is removed, then we must remove the keyguard. It will be put up
- // again when the PUK locked SIM is re-entered.
- case ABSENT:
- // intentional fall-through
// If the SIM is unlocked via a key sequence through the emergency dialer, it will
// move into the READY state and the PUK lock keyguard should be removed.
case READY: {
- KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
// mCallback can be null if onSimStateChanged callback is called when keyguard
// isn't active.
if (mCallback != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index c41ef0e..5766604 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -24,6 +24,7 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.ColorInt;
+import android.annotation.StyleRes;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Color;
@@ -444,9 +445,11 @@
static class KeyguardSliceButton extends Button implements
ConfigurationController.ConfigurationListener {
+ @StyleRes
+ private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
+
public KeyguardSliceButton(Context context) {
- super(context, null /* attrs */, 0 /* styleAttr */,
- com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary);
+ super(context, null /* attrs */, 0 /* styleAttr */, sStyleId);
onDensityOrFontScaleChanged();
setEllipsize(TruncateAt.END);
}
@@ -469,6 +472,11 @@
}
@Override
+ public void onOverlayChanged() {
+ setTextAppearance(sStyleId);
+ }
+
+ @Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
updatePadding();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 416441e..bcd41a0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1936,6 +1936,13 @@
// Even though the subscription is not valid anymore, we need to notify that the
// SIM card was removed so we can update the UI.
becameAbsent = true;
+ for (SimData data : mSimDatas.values()) {
+ // Set the SIM state of all SimData associated with that slot to ABSENT se we
+ // do not move back into PIN/PUK locked and not detect the change below.
+ if (data.slotId == slotId) {
+ data.simState = State.ABSENT;
+ }
+ }
} else {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 9e7c5ba..b461f69 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -19,14 +19,22 @@
import com.android.systemui.plugins.ActivityStarter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Single common instance of ActivityStarter that can be gotten and referenced from anywhere, but
* delegates to an actual implementation such as StatusBar, assuming it exists.
*/
+@Singleton
public class ActivityStarterDelegate implements ActivityStarter {
private ActivityStarter mActualStarter;
+ @Inject
+ public ActivityStarterDelegate() {
+ }
+
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
if (mActualStarter == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 38dadd4..874cdcc 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -325,6 +325,17 @@
.inflate(R.layout.battery_percentage_view, null);
}
+ /**
+ * Updates percent view by removing old one and reinflating if necessary
+ */
+ public void updatePercentView() {
+ if (mBatteryPercentView != null) {
+ removeView(mBatteryPercentView);
+ mBatteryPercentView = null;
+ }
+ updateShowPercent();
+ }
+
private void updatePercentText() {
if (mBatteryPercentView != null) {
mBatteryPercentView.setText(
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 327ffcd..f51004a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -14,20 +14,15 @@
package com.android.systemui;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
-import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
-import android.os.Process;
-import android.os.ServiceManager;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
@@ -36,92 +31,89 @@
import com.android.internal.util.Preconditions;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
-import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
import com.android.systemui.statusbar.policy.IconLogger;
-import com.android.systemui.statusbar.policy.IconLoggerImpl;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.function.Consumer;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import dagger.Lazy;
+import dagger.Subcomponent;
+
/**
* Class to handle ugly dependencies throughout sysui until we determine the
* long-term dependency injection solution.
@@ -143,235 +135,310 @@
/**
* Key for getting a background Looper for background work.
*/
- public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>("background_looper");
+ public static final String BG_LOOPER_NAME = "background_looper";
/**
* Key for getting a background Handler for background work.
*/
- public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>("background_handler");
+ public static final String BG_HANDLER_NAME = "background_handler";
/**
* Key for getting a Handler for receiving time tick broadcasts on.
*/
- public static final DependencyKey<Handler> TIME_TICK_HANDLER =
- new DependencyKey<>("time_tick_handler");
+ public static final String TIME_TICK_HANDLER_NAME = "time_tick_handler";
/**
* Generic handler on the main thread.
*/
- public static final DependencyKey<Handler> MAIN_HANDLER = new DependencyKey<>("main_handler");
+ public static final String MAIN_HANDLER_NAME = "main_handler";
/**
* An email address to send memory leak reports to by default.
*/
- public static final DependencyKey<String> LEAK_REPORT_EMAIL
- = new DependencyKey<>("leak_report_email");
+ public static final String LEAK_REPORT_EMAIL_NAME = "leak_report_email";
+
+ /**
+ * Key for getting a background Looper for background work.
+ */
+ public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
+ /**
+ * Key for getting a background Handler for background work.
+ */
+ public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>(BG_HANDLER_NAME);
+ /**
+ * Key for getting a Handler for receiving time tick broadcasts on.
+ */
+ public static final DependencyKey<Handler> TIME_TICK_HANDLER =
+ new DependencyKey<>(TIME_TICK_HANDLER_NAME);
+ /**
+ * Generic handler on the main thread.
+ */
+ public static final DependencyKey<Handler> MAIN_HANDLER =
+ new DependencyKey<>(MAIN_HANDLER_NAME);
+
+ /**
+ * An email address to send memory leak reports to by default.
+ */
+ public static final DependencyKey<String> LEAK_REPORT_EMAIL =
+ new DependencyKey<>(LEAK_REPORT_EMAIL_NAME);
private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
+ @Inject Lazy<ActivityStarter> mActivityStarter;
+ @Inject Lazy<ActivityStarterDelegate> mActivityStarterDelegate;
+ @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
+ @Inject Lazy<BluetoothController> mBluetoothController;
+ @Inject Lazy<LocationController> mLocationController;
+ @Inject Lazy<RotationLockController> mRotationLockController;
+ @Inject Lazy<NetworkController> mNetworkController;
+ @Inject Lazy<ZenModeController> mZenModeController;
+ @Inject Lazy<HotspotController> mHotspotController;
+ @Inject Lazy<CastController> mCastController;
+ @Inject Lazy<FlashlightController> mFlashlightController;
+ @Inject Lazy<UserSwitcherController> mUserSwitcherController;
+ @Inject Lazy<UserInfoController> mUserInfoController;
+ @Inject Lazy<KeyguardMonitor> mKeyguardMonitor;
+ @Inject Lazy<BatteryController> mBatteryController;
+ @Inject Lazy<ColorDisplayController> mColorDisplayController;
+ @Inject Lazy<ManagedProfileController> mManagedProfileController;
+ @Inject Lazy<NextAlarmController> mNextAlarmController;
+ @Inject Lazy<DataSaverController> mDataSaverController;
+ @Inject Lazy<AccessibilityController> mAccessibilityController;
+ @Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
+ @Inject Lazy<PluginManager> mPluginManager;
+ @Inject Lazy<AssistManager> mAssistManager;
+ @Inject Lazy<SecurityController> mSecurityController;
+ @Inject Lazy<LeakDetector> mLeakDetector;
+ @Inject Lazy<LeakReporter> mLeakReporter;
+ @Inject Lazy<GarbageMonitor> mGarbageMonitor;
+ @Inject Lazy<IconLogger> mIconLogger;
+ @Inject Lazy<TunerService> mTunerService;
+ @Inject Lazy<StatusBarWindowController> mStatusBarWindowController;
+ @Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
+ @Inject Lazy<ConfigurationController> mConfigurationController;
+ @Inject Lazy<StatusBarIconController> mStatusBarIconController;
+ @Inject Lazy<ScreenLifecycle> mScreenLifecycle;
+ @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
+ @Inject Lazy<FragmentService> mFragmentService;
+ @Inject Lazy<ExtensionController> mExtensionController;
+ @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider;
+ @Nullable
+ @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager;
+ @Inject Lazy<VolumeDialogController> mVolumeDialogController;
+ @Inject Lazy<MetricsLogger> mMetricsLogger;
+ @Inject Lazy<AccessibilityManagerWrapper> mAccessibilityManagerWrapper;
+ @Inject Lazy<SysuiColorExtractor> mSysuiColorExtractor;
+ @Inject Lazy<TunablePaddingService> mTunablePaddingService;
+ @Inject Lazy<ForegroundServiceController> mForegroundServiceController;
+ @Inject Lazy<UiOffloadThread> mUiOffloadThread;
+ @Inject Lazy<PowerUI.WarningsUI> mWarningsUI;
+ @Inject Lazy<LightBarController> mLightBarController;
+ @Inject Lazy<IWindowManager> mIWindowManager;
+ @Inject Lazy<OverviewProxyService> mOverviewProxyService;
+ @Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
+ @Inject Lazy<VibratorHelper> mVibratorHelper;
+ @Inject Lazy<IStatusBarService> mIStatusBarService;
+ @Inject Lazy<DisplayMetrics> mDisplayMetrics;
+ @Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
+ @Inject Lazy<KeyguardEnvironment> mKeyguardEnvironment;
+ @Inject Lazy<ShadeController> mShadeController;
+ @Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
+ @Inject Lazy<InitController> mInitController;
+ @Inject Lazy<AppOpsController> mAppOpsController;
+ @Inject Lazy<DisplayNavigationBarController> mDisplayNavigationBarController;
+ @Inject Lazy<StatusBarStateController> mStatusBarStateController;
+ @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
+ @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
+ @Inject Lazy<NotificationGroupManager> mNotificationGroupManager;
+ @Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
+ @Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
+ @Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
+ @Inject Lazy<AmbientPulseManager> mAmbientPulseManager;
+ @Inject Lazy<NotificationBlockingHelperManager> mNotificationBlockingHelperManager;
+ @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
+ @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
+ @Inject Lazy<NotificationListener> mNotificationListener;
+ @Inject Lazy<NotificationLogger> mNotificationLogger;
+ @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
+ @Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
+ @Inject Lazy<NotificationFilter> mNotificationFilter;
+ @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
+ @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
+ @Inject Lazy<SmartReplyController> mSmartReplyController;
+ @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
+ @Inject Lazy<BubbleController> mBubbleController;
+ @Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
+ @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
+ @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
+ @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
+ @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
+ @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
+ @Nullable
+ @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
+
+ @Inject
+ public Dependency() {
+ }
+
@Override
public void start() {
// TODO: Think about ways to push these creation rules out of Dependency to cut down
// on imports.
- mProviders.put(TIME_TICK_HANDLER, () -> {
- HandlerThread thread = new HandlerThread("TimeTick");
- thread.start();
- return new Handler(thread.getLooper());
- });
- mProviders.put(BG_LOOPER, () -> {
- HandlerThread thread = new HandlerThread("SysUiBg",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- return thread.getLooper();
- });
- mProviders.put(BG_HANDLER, () -> new Handler(getDependency(BG_LOOPER)));
- mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
- mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
- mProviders.put(ActivityStarterDelegate.class, () ->
- getDependency(ActivityStarter.class));
+ mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
+ mProviders.put(BG_LOOPER, mBgLooper::get);
+ mProviders.put(BG_HANDLER, mBgHandler::get);
+ mProviders.put(MAIN_HANDLER, mMainHandler::get);
+ mProviders.put(ActivityStarter.class, mActivityStarter::get);
+ mProviders.put(ActivityStarterDelegate.class, mActivityStarterDelegate::get);
- mProviders.put(AsyncSensorManager.class, () ->
- new AsyncSensorManager(mContext.getSystemService(SensorManager.class),
- getDependency(PluginManager.class)));
+ mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
- mProviders.put(SensorPrivacyManager.class, () ->
- mContext.getSystemService(SensorPrivacyManager.class));
+ mProviders.put(BluetoothController.class, mBluetoothController::get);
+ mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
- mProviders.put(BluetoothController.class, () ->
- new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
+ mProviders.put(LocationController.class, mLocationController::get);
- mProviders.put(LocationController.class, () ->
- new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
+ mProviders.put(RotationLockController.class, mRotationLockController::get);
- mProviders.put(RotationLockController.class, () ->
- new RotationLockControllerImpl(mContext));
+ mProviders.put(NetworkController.class, mNetworkController::get);
- mProviders.put(NetworkController.class, () ->
- new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
- getDependency(DeviceProvisionedController.class)));
+ mProviders.put(ZenModeController.class, mZenModeController::get);
- mProviders.put(ZenModeController.class, () ->
- new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));
+ mProviders.put(HotspotController.class, mHotspotController::get);
- mProviders.put(HotspotController.class, () ->
- new HotspotControllerImpl(mContext));
+ mProviders.put(CastController.class, mCastController::get);
- mProviders.put(CastController.class, () ->
- new CastControllerImpl(mContext));
+ mProviders.put(FlashlightController.class, mFlashlightController::get);
- mProviders.put(FlashlightController.class, () ->
- new FlashlightControllerImpl(mContext));
+ mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get);
- mProviders.put(KeyguardMonitor.class, () ->
- new KeyguardMonitorImpl(mContext));
+ mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
- mProviders.put(UserSwitcherController.class, () ->
- new UserSwitcherController(mContext, getDependency(KeyguardMonitor.class),
- getDependency(MAIN_HANDLER), getDependency(ActivityStarter.class)));
+ mProviders.put(UserInfoController.class, mUserInfoController::get);
- mProviders.put(UserInfoController.class, () ->
- new UserInfoControllerImpl(mContext));
+ mProviders.put(BatteryController.class, mBatteryController::get);
- mProviders.put(BatteryController.class, () ->
- new BatteryControllerImpl(mContext));
+ mProviders.put(ColorDisplayController.class, mColorDisplayController::get);
- mProviders.put(ColorDisplayController.class, () ->
- new ColorDisplayController(mContext));
+ mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
- mProviders.put(ManagedProfileController.class, () ->
- new ManagedProfileControllerImpl(mContext));
+ mProviders.put(NextAlarmController.class, mNextAlarmController::get);
- mProviders.put(NextAlarmController.class, () ->
- new NextAlarmControllerImpl(mContext));
+ mProviders.put(DataSaverController.class, mDataSaverController::get);
- mProviders.put(DataSaverController.class, () ->
- get(NetworkController.class).getDataSaverController());
+ mProviders.put(AccessibilityController.class, mAccessibilityController::get);
- mProviders.put(AccessibilityController.class, () ->
- new AccessibilityController(mContext));
+ mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
- mProviders.put(DeviceProvisionedController.class, () ->
- new DeviceProvisionedControllerImpl(mContext));
+ mProviders.put(PluginManager.class, mPluginManager::get);
- mProviders.put(PluginManager.class, () ->
- new PluginManagerImpl(mContext, new PluginInitializerImpl()));
+ mProviders.put(AssistManager.class, mAssistManager::get);
- mProviders.put(AssistManager.class, () ->
- new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
+ mProviders.put(SecurityController.class, mSecurityController::get);
- mProviders.put(SecurityController.class, () ->
- new SecurityControllerImpl(mContext));
+ mProviders.put(LeakDetector.class, mLeakDetector::get);
- mProviders.put(LeakDetector.class, LeakDetector::create);
+ mProviders.put(LEAK_REPORT_EMAIL, mLeakReportEmail::get);
- mProviders.put(LEAK_REPORT_EMAIL, () -> null);
+ mProviders.put(LeakReporter.class, mLeakReporter::get);
- mProviders.put(LeakReporter.class, () -> new LeakReporter(
- mContext,
- getDependency(LeakDetector.class),
- getDependency(LEAK_REPORT_EMAIL)));
+ mProviders.put(GarbageMonitor.class, mGarbageMonitor::get);
- mProviders.put(
- GarbageMonitor.class,
- () ->
- new GarbageMonitor(
- mContext,
- getDependency(BG_LOOPER),
- getDependency(LeakDetector.class),
- getDependency(LeakReporter.class)));
+ mProviders.put(TunerService.class, mTunerService::get);
- mProviders.put(TunerService.class, () ->
- new TunerServiceImpl(mContext));
+ mProviders.put(StatusBarWindowController.class, mStatusBarWindowController::get);
- mProviders.put(StatusBarWindowController.class, () ->
- new StatusBarWindowController(mContext));
+ mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
- mProviders.put(DarkIconDispatcher.class, () ->
- new DarkIconDispatcherImpl(mContext));
+ mProviders.put(ConfigurationController.class, mConfigurationController::get);
- mProviders.put(ConfigurationController.class, () ->
- new ConfigurationControllerImpl(mContext));
+ mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
- mProviders.put(StatusBarIconController.class, () ->
- new StatusBarIconControllerImpl(mContext));
+ mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
- mProviders.put(ScreenLifecycle.class, () ->
- new ScreenLifecycle());
+ mProviders.put(WakefulnessLifecycle.class, mWakefulnessLifecycle::get);
- mProviders.put(WakefulnessLifecycle.class, () ->
- new WakefulnessLifecycle());
+ mProviders.put(FragmentService.class, mFragmentService::get);
- mProviders.put(FragmentService.class, () ->
- new FragmentService());
+ mProviders.put(ExtensionController.class, mExtensionController::get);
- mProviders.put(ExtensionController.class, () ->
- new ExtensionControllerImpl(mContext));
+ mProviders.put(PluginDependencyProvider.class, mPluginDependencyProvider::get);
- mProviders.put(PluginDependencyProvider.class, () ->
- new PluginDependencyProvider(get(PluginManager.class)));
+ mProviders.put(LocalBluetoothManager.class, mLocalBluetoothManager::get);
- mProviders.put(LocalBluetoothManager.class, () ->
- LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER),
- UserHandle.ALL));
+ mProviders.put(VolumeDialogController.class, mVolumeDialogController::get);
- mProviders.put(VolumeDialogController.class, () ->
- new VolumeDialogControllerImpl(mContext));
+ mProviders.put(MetricsLogger.class, mMetricsLogger::get);
- mProviders.put(MetricsLogger.class, () -> new MetricsLogger());
+ mProviders.put(AccessibilityManagerWrapper.class, mAccessibilityManagerWrapper::get);
- mProviders.put(AccessibilityManagerWrapper.class,
- () -> new AccessibilityManagerWrapper(mContext));
+ mProviders.put(SysuiColorExtractor.class, mSysuiColorExtractor::get);
- // Creating a new instance will trigger color extraction.
- // Thankfully this only happens once - during boot - and WallpaperManagerService
- // loads colors from cache.
- mProviders.put(SysuiColorExtractor.class, () -> new SysuiColorExtractor(mContext));
+ mProviders.put(TunablePaddingService.class, mTunablePaddingService::get);
- mProviders.put(TunablePaddingService.class, () -> new TunablePaddingService());
+ mProviders.put(ForegroundServiceController.class, mForegroundServiceController::get);
- mProviders.put(ForegroundServiceController.class,
- () -> new ForegroundServiceControllerImpl(mContext));
+ mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
- mProviders.put(UiOffloadThread.class, UiOffloadThread::new);
+ mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
- mProviders.put(PowerUI.WarningsUI.class, () -> new PowerNotificationWarnings(mContext));
+ mProviders.put(IconLogger.class, mIconLogger::get);
- mProviders.put(IconLogger.class, () -> new IconLoggerImpl(mContext,
- getDependency(BG_LOOPER), getDependency(MetricsLogger.class)));
+ mProviders.put(LightBarController.class, mLightBarController::get);
- mProviders.put(LightBarController.class, () -> new LightBarController(mContext));
+ mProviders.put(IWindowManager.class, mIWindowManager::get);
- mProviders.put(IWindowManager.class, () -> WindowManagerGlobal.getWindowManagerService());
+ mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
- mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
+ mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get);
- mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+ mProviders.put(VibratorHelper.class, mVibratorHelper::get);
- mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext));
+ mProviders.put(IStatusBarService.class, mIStatusBarService::get);
- mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE)));
+ mProviders.put(DisplayMetrics.class, mDisplayMetrics::get);
- // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
- // anywhere it is needed.
- mProviders.put(DisplayMetrics.class, () -> new DisplayMetrics());
+ mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
- mProviders.put(LockscreenGestureLogger.class, () -> new LockscreenGestureLogger());
-
- mProviders.put(KeyguardEnvironment.class, () -> new KeyguardEnvironmentImpl());
- mProviders.put(ShadeController.class, () ->
- SysUiServiceProvider.getComponent(mContext, StatusBar.class));
+ mProviders.put(KeyguardEnvironment.class, mKeyguardEnvironment::get);
+ mProviders.put(ShadeController.class, mShadeController::get);
mProviders.put(NotificationRemoteInputManager.Callback.class,
- () -> new StatusBarRemoteInputCallback(mContext));
+ mNotificationRemoteInputManagerCallback::get);
- mProviders.put(InitController.class, InitController::new);
+ mProviders.put(InitController.class, mInitController::get);
- mProviders.put(AppOpsController.class, () ->
- new AppOpsControllerImpl(mContext, getDependency(BG_LOOPER)));
+ mProviders.put(AppOpsController.class, mAppOpsController::get);
- mProviders.put(DisplayNavigationBarController.class, () ->
- new DisplayNavigationBarController(mContext, getDependency(MAIN_HANDLER)));
+ mProviders.put(DisplayNavigationBarController.class,
+ mDisplayNavigationBarController::get);
- // Put all dependencies above here so the factory can override them if it wants.
- SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
+ mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
+ mProviders.put(NotificationLockscreenUserManager.class,
+ mNotificationLockscreenUserManager::get);
+ mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
+ mProviders.put(NotificationGroupManager.class, mNotificationGroupManager::get);
+ mProviders.put(NotificationGroupAlertTransferHelper.class,
+ mNotificationGroupAlertTransferHelper::get);
+ mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
+ mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
+ mProviders.put(AmbientPulseManager.class, mAmbientPulseManager::get);
+ mProviders.put(NotificationBlockingHelperManager.class,
+ mNotificationBlockingHelperManager::get);
+ mProviders.put(NotificationRemoteInputManager.class,
+ mNotificationRemoteInputManager::get);
+ mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
+ mProviders.put(NotificationListener.class, mNotificationListener::get);
+ mProviders.put(NotificationLogger.class, mNotificationLogger::get);
+ mProviders.put(NotificationViewHierarchyManager.class,
+ mNotificationViewHierarchyManager::get);
+ mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
+ mProviders.put(NotificationFilter.class, mNotificationFilter::get);
+ mProviders.put(NotificationInterruptionStateProvider.class,
+ mNotificationInterruptionStateProvider::get);
+ mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
+ mProviders.put(SmartReplyController.class, mSmartReplyController::get);
+ mProviders.put(RemoteInputQuickSettingsDisabler.class,
+ mRemoteInputQuickSettingsDisabler::get);
+ mProviders.put(BubbleController.class, mBubbleController::get);
+ mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
sDependency = this;
}
@@ -464,10 +531,18 @@
sDependency.destroyDependency(cls, destroy);
}
+ /**
+ * @deprecated see docs/dagger.md
+ */
+ @Deprecated
public static <T> T get(Class<T> cls) {
return sDependency.getDependency(cls);
}
+ /**
+ * @deprecated see docs/dagger.md
+ */
+ @Deprecated
public static <T> T get(DependencyKey<T> cls) {
return sDependency.getDependency(cls);
}
@@ -484,4 +559,20 @@
return mDisplayName;
}
}
+
+ @Subcomponent
+ public interface DependencyInjector {
+ void createSystemUI(Dependency dependency);
+ }
+
+ public static class DependencyCreator implements Injector {
+ @Override
+ public SystemUI apply(Context context) {
+ Dependency dependency = new Dependency();
+ SystemUIFactory.getInstance().getRootComponent()
+ .createDependency()
+ .createSystemUI(dependency);
+ return dependency;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
new file mode 100644
index 0000000..23ef030
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -0,0 +1,227 @@
+/*
+ * 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;
+
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.IconLogger;
+import com.android.systemui.statusbar.policy.IconLoggerImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Maps interfaces to implementations for use with Dagger.
+ */
+@Module
+public abstract class DependencyBinder {
+
+ /**
+ */
+ @Binds
+ public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
+
+ /**
+ */
+ @Binds
+ public abstract BluetoothController provideBluetoothController(
+ BluetoothControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract LocationController provideLocationController(
+ LocationControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract RotationLockController provideRotationLockController(
+ RotationLockControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract NetworkController provideNetworkController(
+ NetworkControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract ZenModeController provideZenModeController(
+ ZenModeControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract HotspotController provideHotspotController(
+ HotspotControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract AppOpsController provideAppOpsController(
+ AppOpsControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
+ StatusBarRemoteInputCallback callbackImpl);
+
+ /**
+ */
+ @Binds
+ public abstract IconLogger provideIconLogger(IconLoggerImpl loggerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract CastController provideCastController(CastControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract FlashlightController provideFlashlightController(
+ FlashlightControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract KeyguardMonitor provideKeyguardMonitor(KeyguardMonitorImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract UserInfoController provideUserInfoContrller(
+ UserInfoControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract BatteryController provideBatteryController(
+ BatteryControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract ManagedProfileController provideManagedProfileController(
+ ManagedProfileControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract NextAlarmController provideNextAlarmController(
+ NextAlarmControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract DeviceProvisionedController provideDeviceProvisionedController(
+ DeviceProvisionedControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract SecurityController provideSecurityController(
+ SecurityControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract DarkIconDispatcher provideDarkIconDispatcher(
+ DarkIconDispatcherImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract StatusBarIconController provideStatusBarIconController(
+ StatusBarIconControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract ExtensionController provideExtensionController(
+ ExtensionControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract VolumeDialogController provideVolumeDialogController(
+ VolumeDialogControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract ForegroundServiceController provideForegroundService(
+ ForegroundServiceControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
new file mode 100644
index 0000000..2b521c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -0,0 +1,176 @@
+/*
+ * 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;
+
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.app.ColorDisplayController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.util.leak.LeakDetector;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies for the root component of sysui injection.
+ * See SystemUI/docs/dagger.md
+ */
+@Module
+public class DependencyProvider {
+
+ @Singleton
+ @Provides
+ @Named(TIME_TICK_HANDLER_NAME)
+ public Handler provideHandler() {
+ HandlerThread thread = new HandlerThread("TimeTick");
+ thread.start();
+ return new Handler(thread.getLooper());
+ }
+
+ @Singleton
+ @Provides
+ @Named(BG_LOOPER_NAME)
+ public Looper provideBgLooper() {
+ HandlerThread thread = new HandlerThread("SysUiBg",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ return thread.getLooper();
+ }
+
+ @Singleton
+ @Provides
+ @Named(BG_HANDLER_NAME)
+ public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) {
+ return new Handler(bgLooper);
+ }
+
+ @Singleton
+ @Provides
+ @Named(MAIN_HANDLER_NAME)
+ public Handler provideMainHandler() {
+ return new Handler(Looper.getMainLooper());
+ }
+
+ @Singleton
+ @Provides
+ public DataSaverController provideDataSaverController(NetworkController networkController) {
+ return networkController.getDataSaverController();
+ }
+
+ @Singleton
+ @Provides
+ @Nullable
+ public LocalBluetoothManager provideLocalBluetoothController(Context context,
+ @Named(BG_HANDLER_NAME) Handler bgHandler) {
+ return LocalBluetoothManager.create(context, bgHandler,
+ UserHandle.ALL);
+ }
+
+ @Singleton
+ @Provides
+ public MetricsLogger provideMetricsLogger() {
+ return new MetricsLogger();
+ }
+
+ @Singleton
+ @Provides
+ public IWindowManager provideIWindowManager() {
+ return WindowManagerGlobal.getWindowManagerService();
+ }
+
+ @Singleton
+ @Provides
+ public IStatusBarService provideIStatusBarService() {
+ return IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ @Singleton
+ @Provides
+ // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
+ // anywhere it is needed.
+ public DisplayMetrics provideDisplayMetrics() {
+ return new DisplayMetrics();
+ }
+
+ @Singleton
+ @Provides
+ public ShadeController provideShadeController(Context context) {
+ return SysUiServiceProvider.getComponent(context, StatusBar.class);
+ }
+
+ @Singleton
+ @Provides
+ public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
+ return context.getSystemService(SensorPrivacyManager.class);
+ }
+
+ @Singleton
+ @Provides
+ public LeakDetector provideLeakDetector() {
+ return LeakDetector.create();
+
+ }
+
+ @Singleton
+ @Provides
+ public ColorDisplayController provideColorDisplayController(Context context) {
+ return new ColorDisplayController(context);
+ }
+
+ @Singleton
+ @Provides
+ public PluginManager providePluginManager(Context context) {
+ return new PluginManagerImpl(context, new PluginInitializerImpl());
+ }
+
+ @Singleton
+ @Provides
+ public ConfigurationController provideConfigurationController(Context context) {
+ return new ConfigurationControllerImpl(context);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
index bab472c..ae446dd 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
@@ -29,9 +29,13 @@
import java.util.Arrays;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Foreground service controller, a/k/a Dianne's Dungeon.
*/
+@Singleton
public class ForegroundServiceControllerImpl
implements ForegroundServiceController {
@@ -45,6 +49,7 @@
private final SparseArray<UserServices> mUserServices = new SparseArray<>();
private final Object mMutex = new Object();
+ @Inject
public ForegroundServiceControllerImpl(Context context) {
mContext = context;
}
diff --git a/packages/SystemUI/src/com/android/systemui/InitController.java b/packages/SystemUI/src/com/android/systemui/InitController.java
index 81d3251..a2dd123 100644
--- a/packages/SystemUI/src/com/android/systemui/InitController.java
+++ b/packages/SystemUI/src/com/android/systemui/InitController.java
@@ -16,10 +16,14 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Created by {@link Dependency} on SystemUI startup. Add tasks which need to be executed only
* after all other dependencies have been created.
*/
+@Singleton
public class InitController {
/**
@@ -29,6 +33,10 @@
private final ArrayList<Runnable> mTasks = new ArrayList<>();
+ @Inject
+ public InitController() {
+ }
+
/**
* Add a task to be executed after {@link Dependency#start()}
* @param runnable the task to be executed
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 30fbef6..78a1246 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -24,6 +24,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Map;
+import java.util.function.Function;
public abstract class SystemUI implements SysUiServiceProvider {
public Context mContext;
@@ -61,4 +62,7 @@
n.addExtras(extras);
}
+
+ public interface Injector extends Function<Context, SystemUI> {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 92aa652..1d8a21d 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -170,7 +170,11 @@
Class cls;
try {
cls = Class.forName(clsName);
- mServices[i] = (SystemUI) cls.newInstance();
+ Object o = cls.newInstance();
+ if (o instanceof SystemUI.Injector) {
+ o = ((SystemUI.Injector) o).apply(this);
+ }
+ mServices[i] = (SystemUI) o;
} catch(ClassNotFoundException ex){
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 867c917..8bac7c5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,9 +16,11 @@
package com.android.systemui;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.Context;
-import android.util.ArrayMap;
import android.util.Log;
import android.view.ViewGroup;
@@ -26,56 +28,57 @@
import com.android.internal.util.function.TriConsumer;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.volume.VolumeDialogComponent;
import java.util.function.Consumer;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
/**
* Class factory to provide customizable SystemUI components.
*/
+@Module
public class SystemUIFactory {
private static final String TAG = "SystemUIFactory";
static SystemUIFactory mFactory;
+ private SystemUIRootComponent mRootComponent;
- public static SystemUIFactory getInstance() {
- return mFactory;
+ public static <T extends SystemUIFactory> T getInstance() {
+ return (T) mFactory;
}
public static void createFromConfig(Context context) {
@@ -88,6 +91,7 @@
Class<?> cls = null;
cls = context.getClassLoader().loadClass(clsName);
mFactory = (SystemUIFactory) cls.newInstance();
+ mFactory.init(context);
} catch (Throwable t) {
Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
throw new RuntimeException(t);
@@ -96,6 +100,18 @@
public SystemUIFactory() {}
+ protected void init(Context context) {
+ mRootComponent = DaggerSystemUIFactory_SystemUIRootComponent.builder()
+ .systemUIFactory(this)
+ .dependencyProvider(new com.android.systemui.DependencyProvider())
+ .contextHolder(new ContextHolder(context))
+ .build();
+ }
+
+ public SystemUIRootComponent getRootComponent() {
+ return mRootComponent;
+ }
+
public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -137,33 +153,89 @@
return new VolumeDialogComponent(systemUi, context);
}
- public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
+ @Singleton
+ @Provides
+ public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
+ return new KeyguardEnvironmentImpl();
+ }
+
+ @Singleton
+ @Provides
+ public NotificationLockscreenUserManager provideNotificationLockscreenUserManager(
Context context) {
- providers.put(StatusBarStateController.class, StatusBarStateController::new);
- providers.put(NotificationLockscreenUserManager.class,
- () -> new NotificationLockscreenUserManagerImpl(context));
- providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
- providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
- providers.put(NotificationGroupAlertTransferHelper.class,
- NotificationGroupAlertTransferHelper::new);
- providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
- providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
- providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
- providers.put(NotificationBlockingHelperManager.class,
- () -> new NotificationBlockingHelperManager(context));
- providers.put(NotificationRemoteInputManager.class,
- () -> new NotificationRemoteInputManager(context));
- providers.put(SmartReplyConstants.class,
- () -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context));
- providers.put(NotificationListener.class, () -> new NotificationListener(context));
- providers.put(NotificationLogger.class, NotificationLogger::new);
- providers.put(NotificationViewHierarchyManager.class,
- () -> new NotificationViewHierarchyManager(context));
- providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
- providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
- providers.put(SmartReplyController.class, () -> new SmartReplyController());
- providers.put(RemoteInputQuickSettingsDisabler.class,
- () -> new RemoteInputQuickSettingsDisabler(context));
- providers.put(BubbleController.class, () -> new BubbleController(context));
+ return new NotificationLockscreenUserManagerImpl(context);
+ }
+
+ @Singleton
+ @Provides
+ public AssistManager provideAssistManager(DeviceProvisionedController controller,
+ Context context) {
+ return new AssistManager(controller, context);
+ }
+
+ @Singleton
+ @Provides
+ public NotificationEntryManager provideNotificationEntryManager(Context context) {
+ return new NotificationEntryManager(context);
+ }
+
+ @Singleton
+ @Provides
+ public EnhancedEstimates provideEnhancedEstimates(Context context) {
+ return new EnhancedEstimatesImpl();
+ }
+
+ @Singleton
+ @Provides
+ @Named(LEAK_REPORT_EMAIL_NAME)
+ @Nullable
+ public String provideLeakReportEmail() {
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public NotificationListener provideNotificationListener(Context context) {
+ return new NotificationListener(context);
+ }
+
+ @Singleton
+ @Provides
+ public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+ Context context) {
+ return new NotificationInterruptionStateProvider(context);
+ }
+
+ @Module
+ protected static class ContextHolder {
+ private Context mContext;
+
+ public ContextHolder(Context context) {
+ mContext = context;
+ }
+
+ @Provides
+ public Context provideContext() {
+ return mContext;
+ }
+ }
+
+ @Singleton
+ @Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
+ ContextHolder.class})
+ public interface SystemUIRootComponent {
+ @Singleton
+ Dependency.DependencyInjector createDependency();
+
+ /**
+ * FragmentCreator generates all Fragments that need injection.
+ */
+ @Singleton
+ FragmentService.FragmentCreator createFragmentCreator();
+
+ /**
+ * ViewCreator generates all Views that need injection.
+ */
+ InjectionInflationController.ViewCreator createViewCreator();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
index 82fd9b3..a726b42 100644
--- a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
+++ b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
@@ -20,14 +20,22 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Thread that offloads work from the UI thread but that is still perceptible to the user, so the
* priority is the same as the main thread.
*/
+@Singleton
public class UiOffloadThread {
private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
+ @Inject
+ public UiOffloadThread() {
+ }
+
public Future<?> submit(Runnable runnable) {
return mExecutorService.submit(runnable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 906a210..e3bcb37 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.appops;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Handler;
@@ -32,34 +34,46 @@
import java.util.List;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/**
* Controller to keep track of applications that have requested access to given App Ops
*
* It can be subscribed to with callbacks. Additionally, it passes on the information to
* NotificationPresenter to be displayed to the user.
*/
+@Singleton
public class AppOpsControllerImpl implements AppOpsController,
- AppOpsManager.OnOpActiveChangedListener {
+ AppOpsManager.OnOpActiveChangedListener,
+ AppOpsManager.OnOpNotedListener {
- private static final long LOCATION_TIME_DELAY_MS = 5000;
+ private static final long NOTED_OP_TIME_DELAY_MS = 5000;
private static final String TAG = "AppOpsControllerImpl";
private static final boolean DEBUG = false;
private final Context mContext;
- protected final AppOpsManager mAppOps;
- private final H mBGHandler;
+ private final AppOpsManager mAppOps;
+ private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
+
@GuardedBy("mActiveItems")
private final List<AppOpItem> mActiveItems = new ArrayList<>();
+ @GuardedBy("mNotedItems")
+ private final List<AppOpItem> mNotedItems = new ArrayList<>();
- protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
+ protected static final int[] OPS = new int[] {
+ AppOpsManager.OP_CAMERA,
AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION};
+ AppOpsManager.OP_FINE_LOCATION
+ };
- public AppOpsControllerImpl(Context context, Looper bgLooper) {
+ @Inject
+ public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
mContext = context;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mBGHandler = new H(bgLooper);
@@ -70,11 +84,18 @@
}
@VisibleForTesting
+ protected void setBGHandler(H handler) {
+ mBGHandler = handler;
+ }
+
+ @VisibleForTesting
protected void setListening(boolean listening) {
if (listening) {
mAppOps.startWatchingActive(OPS, this);
+ mAppOps.startWatchingNoted(OPS, this);
} else {
mAppOps.stopWatchingActive(this);
+ mAppOps.stopWatchingNoted(this);
}
}
@@ -124,10 +145,11 @@
if (mCallbacks.isEmpty()) setListening(false);
}
- private AppOpItem getAppOpItem(int code, int uid, String packageName) {
- final int itemsQ = mActiveItems.size();
+ private AppOpItem getAppOpItem(List<AppOpItem> appOpList, int code, int uid,
+ String packageName) {
+ final int itemsQ = appOpList.size();
for (int i = 0; i < itemsQ; i++) {
- AppOpItem item = mActiveItems.get(i);
+ AppOpItem item = appOpList.get(i);
if (item.getCode() == code && item.getUid() == uid
&& item.getPackageName().equals(packageName)) {
return item;
@@ -138,39 +160,59 @@
private boolean updateActives(int code, int uid, String packageName, boolean active) {
synchronized (mActiveItems) {
- AppOpItem item = getAppOpItem(code, uid, packageName);
+ AppOpItem item = getAppOpItem(mActiveItems, code, uid, packageName);
if (item == null && active) {
item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
mActiveItems.add(item);
- if (code == AppOpsManager.OP_COARSE_LOCATION
- || code == AppOpsManager.OP_FINE_LOCATION) {
- mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
- }
if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
return true;
} else if (item != null && !active) {
mActiveItems.remove(item);
if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
return true;
- } else if (item != null && active
- && (code == AppOpsManager.OP_COARSE_LOCATION
- || code == AppOpsManager.OP_FINE_LOCATION)) {
- mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
- return true;
}
return false;
}
}
+ private void removeNoted(int code, int uid, String packageName) {
+ AppOpItem item;
+ synchronized (mNotedItems) {
+ item = getAppOpItem(mNotedItems, code, uid, packageName);
+ if (item == null) return;
+ mNotedItems.remove(item);
+ if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
+ }
+ notifySuscribers(code, uid, packageName, false);
+ }
+
+ private void addNoted(int code, int uid, String packageName) {
+ AppOpItem item;
+ synchronized (mNotedItems) {
+ item = getAppOpItem(mNotedItems, code, uid, packageName);
+ if (item == null) {
+ item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
+ mNotedItems.add(item);
+ if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
+ }
+ }
+ mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
+ }
+
/**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
* @return List of active AppOps information
*/
public List<AppOpItem> getActiveAppOps() {
+ ArrayList<AppOpItem> active;
synchronized (mActiveItems) {
- return new ArrayList<>(mActiveItems);
+ active = new ArrayList<>(mActiveItems);
}
+ synchronized (mNotedItems) {
+ active.addAll(mNotedItems);
+ }
+ return active;
}
/**
@@ -192,19 +234,44 @@
}
}
}
+ synchronized (mNotedItems) {
+ final int numNotedItems = mNotedItems.size();
+ for (int i = 0; i < numNotedItems; i++) {
+ AppOpItem item = mNotedItems.get(i);
+ if (UserHandle.getUserId(item.getUid()) == userId) {
+ list.add(item);
+ }
+ }
+ }
return list;
}
@Override
public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
if (updateActives(code, uid, packageName, active)) {
+ notifySuscribers(code, uid, packageName, active);
+ }
+ }
+
+ @Override
+ public void onOpNoted(int code, int uid, String packageName, int result) {
+ if (DEBUG) {
+ Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
+ }
+ if (result != AppOpsManager.MODE_ALLOWED) return;
+ addNoted(code, uid, packageName);
+ notifySuscribers(code, uid, packageName, true);
+ }
+
+ private void notifySuscribers(int code, int uid, String packageName, boolean active) {
+ if (mCallbacksByCode.containsKey(code)) {
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
}
}
}
- private final class H extends Handler {
+ protected final class H extends Handler {
H(Looper looper) {
super(looper);
}
@@ -214,8 +281,7 @@
postDelayed(new Runnable() {
@Override
public void run() {
- onOpActiveChanged(item.getCode(), item.getUid(),
- item.getPackageName(), false);
+ removeNoted(item.getCode(), item.getUid(), item.getPackageName());
}
}, item, timeToRemoval);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1e91ef3..c8595eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,12 +42,16 @@
import java.util.HashMap;
import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Bubbles are a special type of content that can "float" on top of other apps or System UI.
* Bubbles can be expanded to show more content.
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
+@Singleton
public class BubbleController {
private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
@@ -117,6 +121,7 @@
void onBubbleExpandChanged(boolean isExpanding, float amount);
}
+ @Inject
public BubbleController(Context context) {
mContext = context;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 3c8a461d..fdf18ce 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -39,9 +39,13 @@
import java.io.PrintWriter;
import java.util.Arrays;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* ColorExtractor aware of wallpaper visibility
*/
+@Singleton
public class SysuiColorExtractor extends ColorExtractor implements Dumpable {
private static final String TAG = "SysuiColorExtractor";
private boolean mWallpaperVisible;
@@ -49,6 +53,7 @@
// Colors to return when the wallpaper isn't visible
private final GradientColors mWpHiddenColors;
+ @Inject
public SysuiColorExtractor(Context context) {
this(context, new Tonal(context), true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 1e61a77..461aef1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -17,7 +17,6 @@
package com.android.systemui.doze;
import android.annotation.NonNull;
-import android.app.PendingIntent;
/**
* Interface the doze service uses to communicate with the rest of system UI.
@@ -34,7 +33,6 @@
boolean isProvisioned();
boolean isBlockingDoze();
- void startPendingIntentDismissingKeyguard(PendingIntent intent);
void extendPulse();
void setAnimateWakeup(boolean animateWakeup);
@@ -47,12 +45,6 @@
void onIgnoreTouchWhilePulsing(boolean ignore);
- /**
- * If the device was waken up by a passive interrupt that will show the lock screen without
- * expanding the notification panel/shade.
- */
- void setPassiveInterrupt(boolean lightInterrupt);
-
interface Callback {
/**
* Called when a high priority notification is added.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1da8976..1e228f2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -151,7 +151,6 @@
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
} else if (isPickup) {
- mDozeHost.setPassiveInterrupt(true);
mMachine.wakeUp();
} else {
mDozeHost.extendPulse();
@@ -221,7 +220,6 @@
case INITIALIZED:
mBroadcastReceiver.register(mContext);
mDozeHost.addCallback(mHostCallback);
- mDozeHost.setPassiveInterrupt(false);
checkTriggersAtInit();
break;
case DOZE:
@@ -231,7 +229,6 @@
mDozeSensors.reregisterAllSensors();
}
mDozeSensors.setListening(true);
- mDozeHost.setPassiveInterrupt(false);
if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
onWakeScreen(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java b/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java
index ebfafce..70143e2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java
@@ -106,7 +106,6 @@
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) {
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 60e39b2..30f6e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -41,6 +41,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
@@ -186,6 +188,13 @@
mFragments.dispatchDestroy();
}
+ /**
+ * Creates a fragment that requires injection.
+ */
+ public <T> T create(Class<T> fragmentCls) {
+ return (T) mPlugins.instantiate(mContext, fragmentCls.getName(), null);
+ }
+
public interface FragmentListener {
void onFragmentViewCreated(String tag, Fragment fragment);
@@ -294,13 +303,36 @@
Fragment instantiate(Context context, String className, Bundle arguments) {
Context extensionContext = mExtensionLookup.get(className);
if (extensionContext != null) {
- Fragment f = Fragment.instantiate(extensionContext, className, arguments);
+ Fragment f = instantiateWithInjections(extensionContext, className, arguments);
if (f instanceof Plugin) {
((Plugin) f).onCreate(mContext, extensionContext);
}
return f;
}
- return Fragment.instantiate(context, className, arguments);
+ return instantiateWithInjections(context, className, arguments);
+ }
+
+ private Fragment instantiateWithInjections(Context context, String className,
+ Bundle args) {
+ Method method = mManager.getInjectionMap().get(className);
+ if (method != null) {
+ try {
+ Fragment f = (Fragment) method.invoke(mManager.getFragmentCreator());
+ // Setup the args, taken from Fragment#instantiate.
+ if (args != null) {
+ args.setClassLoader(f.getClass().getClassLoader());
+ f.setArguments(args);
+ }
+ return f;
+ } catch (IllegalAccessException e) {
+ throw new Fragment.InstantiationException("Unable to instantiate " + className,
+ e);
+ } catch (InvocationTargetException e) {
+ throw new Fragment.InstantiationException("Unable to instantiate " + className,
+ e);
+ }
+ }
+ return Fragment.instantiate(context, className, args);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index bf7d629..8dbaf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -14,6 +14,7 @@
package com.android.systemui.fragments;
+import android.app.Fragment;
import android.content.res.Configuration;
import android.os.Handler;
import android.util.ArrayMap;
@@ -21,20 +22,56 @@
import com.android.systemui.ConfigurationChangedReceiver;
import com.android.systemui.Dumpable;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.statusbar.phone.NavigationBarFragment;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Subcomponent;
/**
* Holds a map of root views to FragmentHostStates and generates them as needed.
* Also dispatches the configuration changes to all current FragmentHostStates.
*/
+@Singleton
public class FragmentService implements ConfigurationChangedReceiver, Dumpable {
private static final String TAG = "FragmentService";
private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
+ private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
private final Handler mHandler = new Handler();
+ private final FragmentCreator mFragmentCreator;
+
+ @Inject
+ public FragmentService(SystemUIFactory.SystemUIRootComponent rootComponent) {
+ mFragmentCreator = rootComponent.createFragmentCreator();
+ initInjectionMap();
+ }
+
+ ArrayMap<String, Method> getInjectionMap() {
+ return mInjectionMap;
+ }
+
+ FragmentCreator getFragmentCreator() {
+ return mFragmentCreator;
+ }
+
+ private void initInjectionMap() {
+ for (Method method : FragmentCreator.class.getDeclaredMethods()) {
+ if (Fragment.class.isAssignableFrom(method.getReturnType())
+ && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+ mInjectionMap.put(method.getReturnType().getName(), method);
+ }
+ }
+ }
public FragmentHostManager getFragmentHostManager(View view) {
View root = view.getRootView();
@@ -74,6 +111,21 @@
}
}
+ /**
+ * The subcomponent of dagger that holds all fragments that need injection.
+ */
+ @Subcomponent
+ public interface FragmentCreator {
+ /**
+ * Inject a NavigationBarFragment.
+ */
+ NavigationBarFragment createNavigationBarFragment();
+ /**
+ * Inject a QSFragment.
+ */
+ QSFragment createQSFragment();
+ }
+
private class FragmentHostState {
private final View mView;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 201c7e6..6a0e8ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard;
+import android.annotation.AnyThread;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -24,14 +25,20 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Typeface;
+import android.graphics.drawable.Icon;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
+import android.media.MediaMetadata;
import android.net.Uri;
import android.os.Handler;
import android.os.Trace;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
import android.text.TextUtils;
+import android.text.style.StyleSpan;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -41,7 +48,11 @@
import androidx.slice.builders.SliceAction;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -49,19 +60,24 @@
import java.util.Date;
import java.util.Locale;
+import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
/**
* Simple Slice provider that shows the current date.
*/
public class KeyguardSliceProvider extends SliceProvider implements
- NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback {
+ NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback,
+ NotificationMediaManager.MediaListener {
+ private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
public static final String KEYGUARD_NEXT_ALARM_URI =
"content://com.android.systemui.keyguard/alarm";
public static final String KEYGUARD_DND_URI = "content://com.android.systemui.keyguard/dnd";
+ public static final String KEYGUARD_MEDIA_URI =
+ "content://com.android.systemui.keyguard/media";
public static final String KEYGUARD_ACTION_URI =
"content://com.android.systemui.keyguard/action";
@@ -77,6 +93,7 @@
protected final Uri mDateUri;
protected final Uri mAlarmUri;
protected final Uri mDndUri;
+ protected final Uri mMediaUri;
private final Date mCurrentTime = new Date();
private final Handler mHandler;
private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
@@ -91,6 +108,8 @@
protected ContentResolver mContentResolver;
private AlarmManager.AlarmClockInfo mNextAlarmInfo;
private PendingIntent mPendingIntent;
+ protected NotificationMediaManager mMediaManager;
+ protected MediaMetadata mMediaMetaData;
/**
* Receiver responsible for time ticking and updating the date format.
@@ -100,21 +119,36 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (Intent.ACTION_TIME_TICK.equals(action)
- || Intent.ACTION_DATE_CHANGED.equals(action)
- || Intent.ACTION_TIME_CHANGED.equals(action)
- || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
- || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
- if (Intent.ACTION_LOCALE_CHANGED.equals(action)
- || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
- // need to get a fresh date format
- mHandler.post(KeyguardSliceProvider.this::cleanDateFormat);
+ if (Intent.ACTION_DATE_CHANGED.equals(action)) {
+ synchronized (this) {
+ updateClockLocked();
}
- mHandler.post(KeyguardSliceProvider.this::updateClock);
+ } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+ synchronized (this) {
+ cleanDateFormatLocked();
+ }
}
}
};
+ @VisibleForTesting
+ final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onTimeChanged() {
+ synchronized (this) {
+ updateClockLocked();
+ }
+ }
+
+ @Override
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ synchronized (this) {
+ cleanDateFormatLocked();
+ }
+ }
+ };
+
public KeyguardSliceProvider() {
this(new Handler());
}
@@ -130,22 +164,62 @@
mDateUri = Uri.parse(KEYGUARD_DATE_URI);
mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
mDndUri = Uri.parse(KEYGUARD_DND_URI);
+ mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
}
+ public void initDependencies() {
+ mMediaManager = Dependency.get(NotificationMediaManager.class);
+ mMediaManager.addCallback(this);
+ }
+
+ @AnyThread
@Override
public Slice onBindSlice(Uri sliceUri) {
Trace.beginSection("KeyguardSliceProvider#onBindSlice");
- ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
- builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
- addNextAlarm(builder);
- addZenMode(builder);
- addPrimaryAction(builder);
- Slice slice = builder.build();
+ Slice slice;
+ synchronized (this) {
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
+ if (mMediaMetaData != null) {
+ addMediaLocked(builder);
+ } else {
+ builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+ addNextAlarmLocked(builder);
+ addZenModeLocked(builder);
+ }
+ addPrimaryActionLocked(builder);
+ slice = builder.build();
+ }
Trace.endSection();
return slice;
}
- protected void addPrimaryAction(ListBuilder builder) {
+ protected void addMediaLocked(ListBuilder listBuilder) {
+ if (mMediaMetaData != null) {
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
+ if (TextUtils.isEmpty(title)) {
+ title = getContext().getResources().getString(R.string.music_controls_no_title);
+ }
+ builder.append(title);
+ builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+
+ CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
+ if (!TextUtils.isEmpty(album)) {
+ builder.append(" ").append(album);
+ }
+
+ RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
+ Icon notificationIcon = mMediaManager.getMediaIcon();
+ if (notificationIcon != null) {
+ IconCompat icon = IconCompat.createFromIcon(notificationIcon);
+ mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
+ }
+
+ listBuilder.addRow(mediaBuilder);
+ }
+ }
+
+ protected void addPrimaryActionLocked(ListBuilder builder) {
// Add simple action because API requires it; Keyguard handles presenting
// its own slices so this action + icon are actually never used.
IconCompat icon = IconCompat.createWithResource(getContext(),
@@ -157,7 +231,7 @@
builder.addRow(primaryActionRow);
}
- protected void addNextAlarm(ListBuilder builder) {
+ protected void addNextAlarmLocked(ListBuilder builder) {
if (TextUtils.isEmpty(mNextAlarm)) {
return;
}
@@ -173,7 +247,7 @@
* Add zen mode (DND) icon to slice if it's enabled.
* @param builder The slice builder.
*/
- protected void addZenMode(ListBuilder builder) {
+ protected void addZenModeLocked(ListBuilder builder) {
if (!isDndOn()) {
return;
}
@@ -204,33 +278,35 @@
mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
KeyguardSliceProvider.sInstance = this;
registerClockUpdate();
- updateClock();
+ updateClockLocked();
return true;
}
@Override
public void onZenChanged(int zen) {
- mContentResolver.notifyChange(mSliceUri, null /* observer */);
+ notifyChange();
}
@Override
public void onConfigChanged(ZenModeConfig config) {
- mContentResolver.notifyChange(mSliceUri, null /* observer */);
+ notifyChange();
}
private void updateNextAlarm() {
- if (withinNHours(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
- String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
- ActivityManager.getCurrentUser()) ? "HH:mm" : "h:mm";
- mNextAlarm = android.text.format.DateFormat.format(pattern,
- mNextAlarmInfo.getTriggerTime()).toString();
- } else {
- mNextAlarm = "";
+ synchronized (this) {
+ if (withinNHoursLocked(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
+ String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
+ ActivityManager.getCurrentUser()) ? "HH:mm" : "h:mm";
+ mNextAlarm = android.text.format.DateFormat.format(pattern,
+ mNextAlarmInfo.getTriggerTime()).toString();
+ } else {
+ mNextAlarm = "";
+ }
}
- mContentResolver.notifyChange(mSliceUri, null /* observer */);
+ notifyChange();
}
- private boolean withinNHours(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
+ private boolean withinNHoursLocked(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
if (alarmClockInfo == null) {
return false;
}
@@ -244,34 +320,37 @@
* changing the date/time via the settings app.
*/
private void registerClockUpdate() {
- if (mRegistered) {
- return;
- }
+ synchronized (this) {
+ if (mRegistered) {
+ return;
+ }
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_DATE_CHANGED);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
- null /* scheduler */);
- mRegistered = true;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DATE_CHANGED);
+ filter.addAction(Intent.ACTION_LOCALE_CHANGED);
+ getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
+ null /* scheduler */);
+ getKeyguardUpdateMonitor().registerCallback(mKeyguardUpdateMonitorCallback);
+ mRegistered = true;
+ }
}
@VisibleForTesting
boolean isRegistered() {
- return mRegistered;
- }
-
- protected void updateClock() {
- final String text = getFormattedDate();
- if (!text.equals(mLastText)) {
- mLastText = text;
- mContentResolver.notifyChange(mSliceUri, null /* observer */);
+ synchronized (this) {
+ return mRegistered;
}
}
- protected String getFormattedDate() {
+ protected void updateClockLocked() {
+ final String text = getFormattedDateLocked();
+ if (!text.equals(mLastText)) {
+ mLastText = text;
+ notifyChange();
+ }
+ }
+
+ protected String getFormattedDateLocked() {
if (mDateFormat == null) {
final Locale l = Locale.getDefault();
DateFormat format = DateFormat.getInstanceForSkeleton(mDatePattern, l);
@@ -283,21 +362,40 @@
}
@VisibleForTesting
- void cleanDateFormat() {
+ void cleanDateFormatLocked() {
mDateFormat = null;
}
@Override
public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
- mNextAlarmInfo = nextAlarm;
- mAlarmManager.cancel(mUpdateNextAlarm);
+ synchronized (this) {
+ mNextAlarmInfo = nextAlarm;
+ mAlarmManager.cancel(mUpdateNextAlarm);
- long triggerAt = mNextAlarmInfo == null ? -1 : mNextAlarmInfo.getTriggerTime()
- - TimeUnit.HOURS.toMillis(ALARM_VISIBILITY_HOURS);
- if (triggerAt > 0) {
- mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm",
- mUpdateNextAlarm, mHandler);
+ long triggerAt = mNextAlarmInfo == null ? -1 : mNextAlarmInfo.getTriggerTime()
+ - TimeUnit.HOURS.toMillis(ALARM_VISIBILITY_HOURS);
+ if (triggerAt > 0) {
+ mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm",
+ mUpdateNextAlarm, mHandler);
+ }
}
updateNextAlarm();
}
+
+ @VisibleForTesting
+ protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
+ return KeyguardUpdateMonitor.getInstance(getContext());
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ synchronized (this) {
+ mMediaMetaData = metadata;
+ }
+ notifyChange();
+ }
+
+ protected void notifyChange() {
+ mContentResolver.notifyChange(mSliceUri, null /* observer */);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b133346..323cf1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -445,8 +445,7 @@
boolean simWasLocked;
synchronized (KeyguardViewMediator.this) {
IccCardConstants.State lastState = mLastSimStates.get(slotId);
- simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED)
- && simState == READY;
+ simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED);
mLastSimStates.append(slotId, simState);
}
@@ -471,6 +470,8 @@
// so we should only lock when they are ABSENT.
onSimAbsentLocked();
if (simWasLocked) {
+ if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+ + "previous state was locked. Reset the state.");
resetStateLocked();
}
}
@@ -507,6 +508,8 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
if (mShowing && simWasLocked) {
+ if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
+ + "previous state was locked. Reset the state.");
resetStateLocked();
}
mLockWhenSimRemoved = true;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
index b6fce44..834bca5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
@@ -23,9 +23,13 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Tracks the screen lifecycle.
*/
+@Singleton
public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> implements Dumpable {
public static final int SCREEN_OFF = 0;
@@ -35,6 +39,10 @@
private int mScreenState = SCREEN_OFF;
+ @Inject
+ public ScreenLifecycle() {
+ }
+
public int getScreenState() {
return mScreenState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 59c7f23..52a0214 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -23,9 +23,13 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Tracks the wakefulness lifecycle.
*/
+@Singleton
public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements
Dumpable {
@@ -36,6 +40,10 @@
private int mWakefulness = WAKEFULNESS_ASLEEP;
+ @Inject
+ public WakefulnessLifecycle() {
+ }
+
public int getWakefulness() {
return mWakefulness;
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index 03daa95..6949531 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -20,11 +20,20 @@
import com.android.systemui.plugins.PluginDependency.DependencyProvider;
import com.android.systemui.shared.plugins.PluginManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class PluginDependencyProvider extends DependencyProvider {
private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>();
private final PluginManager mManager;
+ /**
+ */
+ @Inject
public PluginDependencyProvider(PluginManager manager) {
mManager = manager;
PluginDependency.sProvider = this;
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index b722f9f..50dda1c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -58,6 +58,12 @@
import java.util.Locale;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
private static final boolean DEBUG = PowerUI.DEBUG;
@@ -136,6 +142,9 @@
private SystemUIDialog mHighTempDialog;
private SystemUIDialog mThermalShutdownDialog;
+ /**
+ */
+ @Inject
public PowerNotificationWarnings(Context context) {
mContext = context;
mNoMan = mContext.getSystemService(NotificationManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index bbdae29..3991c19 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -106,12 +106,12 @@
val appName = item.findViewById(R.id.app_name) as TextView
val icons = item.findViewById(R.id.icons) as LinearLayout
- var lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
+ val lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
gravity = Gravity.CENTER_VERTICAL
marginStart = iconMargin
}
- app.icon?.let {
+ app.icon.let {
appIcon.setImageDrawable(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 519df19..2894621 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -29,7 +29,9 @@
init {
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
- .sortedWith(compareBy({ -it.second.size }, { it.first }))
+ .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
+ { it.second.min() }, // Sort by "smallest" AppOpp (Location is largest)
+ { it.first })) // Sort alphabetically bt App Name
types = itemsList.map { it.privacyType }.distinct().sorted()
val singleApp = appsAndTypes.size == 1
app = if (singleApp) appsAndTypes[0].first else null
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 85e99f0..9252167 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -24,8 +24,8 @@
enum class PrivacyType(val nameId: Int, val iconId: Int) {
TYPE_CAMERA(R.string.privacy_type_camera, R.drawable.stat_sys_camera),
- TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
- TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none);
+ TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none),
+ TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location);
fun getName(context: Context) = context.resources.getString(nameId)
@@ -44,7 +44,7 @@
return applicationName.compareTo(other.applicationName)
}
- var icon: Drawable? = null
+ var icon: Drawable = context.getDrawable(android.R.drawable.sym_def_app_icon)
var applicationName: String
init {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 268462e..d5b807d 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -29,6 +29,7 @@
import com.android.systemui.Dependency
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
+import com.android.systemui.R
class PrivacyItemController(val context: Context, val callback: Callback) {
@@ -41,15 +42,17 @@
Intent.ACTION_MANAGED_PROFILE_ADDED,
Intent.ACTION_MANAGED_PROFILE_REMOVED)
const val TAG = "PrivacyItemController"
+ const val SYSTEM_UID = 1000
}
-
private var privacyList = emptyList<PrivacyItem>()
+
private val appOpsController = Dependency.get(AppOpsController::class.java)
private val userManager = context.getSystemService(UserManager::class.java)
private var currentUserIds = emptyList<Int>()
private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
private var listening = false
+ val systemApp = PrivacyApplication(context.getString(R.string.device_services), context)
private val notifyChanges = Runnable {
callback.privacyChanged(privacyList)
@@ -115,7 +118,7 @@
private fun updatePrivacyList() {
privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
- .mapNotNull { toPrivacyItem(it) }
+ .mapNotNull { toPrivacyItem(it) }.distinct()
}
private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
@@ -126,6 +129,7 @@
AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
else -> return null
}
+ if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp)
val app = PrivacyApplication(appOpItem.packageName, context)
return PrivacyItem(type, app)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 8903a38..aba9bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -272,10 +272,17 @@
public void updateEverything() {
post(() -> {
updateVisibilities();
+ updateClickabilities();
setClickable(false);
});
}
+ private void updateClickabilities() {
+ mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
+ mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
+ mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
+ }
+
private void updateVisibilities() {
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 2acbea4..bedba9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,7 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.app.Fragment;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -35,7 +35,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.R.id;
@@ -46,8 +45,12 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.LifecycleFragment;
-public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
+import javax.inject.Inject;
+
+public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks {
private static final String TAG = "QS";
private static final boolean DEBUG = false;
private static final String EXTRA_EXPANDED = "expanded";
@@ -74,13 +77,24 @@
private float mLastQSExpansion = -1;
private boolean mQsDisabled;
- private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler =
- Dependency.get(RemoteInputQuickSettingsDisabler.class);
+ private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+ private final InjectionInflationController mInjectionInflater;
+
+ @Inject
+ public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+ InjectionInflationController injectionInflater,
+ Context context) {
+ mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
+ mInjectionInflater = injectionInflater;
+ SysUiServiceProvider.getComponent(context, CommandQueue.class)
+ .observe(getLifecycle(), this);
+ }
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
- inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
+ inflater = mInjectionInflater.injectable(
+ inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)));
return inflater.inflate(R.layout.qs_panel, container, false);
}
@@ -108,13 +122,6 @@
mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
}
}
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
- }
-
- @Override
- public void onDestroyView() {
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this);
- super.onDestroyView();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 3cecff0..bec027f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,8 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.ColorInt;
@@ -58,7 +60,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
@@ -84,6 +85,9 @@
import java.util.Locale;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Named;
+
/**
* View that contains the top-most bits of the screen (primarily the status bar with date, time, and
* battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
@@ -102,6 +106,11 @@
public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
private final Handler mHandler = new Handler();
+ private final BatteryController mBatteryController;
+ private final NextAlarmController mAlarmController;
+ private final ZenModeController mZenController;
+ private final StatusBarIconController mStatusBarIconController;
+ private final ActivityStarter mActivityStarter;
private QSPanel mQsPanel;
@@ -141,8 +150,6 @@
private TextView mBatteryRemainingText;
private boolean mShowBatteryPercentAndEstimate;
- private NextAlarmController mAlarmController;
- private ZenModeController mZenController;
private PrivacyItemController mPrivacyItemController;
/** Counts how many times the long press tooltip has been shown to the user. */
private int mShownCount;
@@ -172,10 +179,17 @@
}
};
- public QuickStatusBarHeader(Context context, AttributeSet attrs) {
+ @Inject
+ public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+ NextAlarmController nextAlarmController, ZenModeController zenModeController,
+ BatteryController batteryController, StatusBarIconController statusBarIconController,
+ ActivityStarter activityStarter) {
super(context, attrs);
- mAlarmController = Dependency.get(NextAlarmController.class);
- mZenController = Dependency.get(ZenModeController.class);
+ mAlarmController = nextAlarmController;
+ mZenController = zenModeController;
+ mBatteryController = batteryController;
+ mStatusBarIconController = statusBarIconController;
+ mActivityStarter = activityStarter;
mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
mShownCount = getStoredShownCount();
}
@@ -405,15 +419,13 @@
if (!mShowBatteryPercentAndEstimate) {
return;
}
- mBatteryRemainingText.setText(
- Dependency.get(BatteryController.class).getEstimatedTimeRemainingString());
+ mBatteryRemainingText.setText(mBatteryController.getEstimatedTimeRemainingString());
}
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
mHeaderQsPanel.setExpanded(expanded);
- updateEverything();
}
/**
@@ -472,7 +484,7 @@
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
+ mStatusBarIconController.addIconGroup(mIconManager);
requestApplyInsets();
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mPercentSettingObserver,
@@ -515,7 +527,7 @@
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
- Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
+ mStatusBarIconController.removeIconGroup(mIconManager);
mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
super.onDetachedFromWindow();
}
@@ -544,10 +556,10 @@
@Override
public void onClick(View v) {
if (v == mClockView) {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS),0);
} else if (v == mBatteryMeterView) {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Intent.ACTION_POWER_USAGE_SUMMARY),0);
} else if (v == mPrivacyChip) {
Handler mUiHandler = new Handler(Looper.getMainLooper());
@@ -685,10 +697,6 @@
.start();
}
- public void updateEverything() {
- post(() -> setClickable(false));
- }
-
public void setQSPanel(final QSPanel qsPanel) {
mQsPanel = qsPanel;
setupHost(qsPanel.getHost());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 9d2be39..4dcacd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -87,6 +87,8 @@
if (current != null) {
// The setting QS_TILES is not populated immediately upon Factory Reset
possibleTiles.addAll(Arrays.asList(current.split(",")));
+ } else {
+ current = "";
}
String[] stockSplit = stock.split(",");
for (String spec : stockSplit) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 8906665..466c808 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -183,6 +183,7 @@
public void updateState(Tile tile) {
mTile.setIcon(tile.getIcon());
mTile.setLabel(tile.getLabel());
+ mTile.setSubtitle(tile.getSubtitle());
mTile.setContentDescription(tile.getContentDescription());
mTile.setState(tile.getState());
}
@@ -322,6 +323,14 @@
return null;
};
state.label = mTile.getLabel();
+
+ CharSequence subtitle = mTile.getSubtitle();
+ if (subtitle != null && subtitle.length() > 0) {
+ state.secondaryLabel = subtitle;
+ } else {
+ state.secondaryLabel = null;
+ }
+
if (mTile.getContentDescription() != null) {
state.contentDescription = mTile.getContentDescription();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 12b6f67..538e0f0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -63,9 +63,13 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Class to send information from overview to launcher with a binder.
*/
+@Singleton
public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
@@ -338,6 +342,7 @@
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
= this::cleanupAfterDeath;
+ @Inject
public OverviewProxyService(Context context) {
mContext = context;
mHandler = new Handler();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 8821679..9bfd4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -28,17 +28,22 @@
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manager which handles high priority notifications that should "pulse" in when the device is
* dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly
* before automatically dismissing the alert.
*/
+@Singleton
public class AmbientPulseManager extends AlertingNotificationManager {
protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
@VisibleForTesting
protected long mExtensionTime;
+ @Inject
public AmbientPulseManager(@NonNull final Context context) {
Resources resources = context.getResources();
mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
index 2f26109..78172f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
@@ -18,6 +18,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -32,9 +34,14 @@
import com.android.systemui.statusbar.phone.NavigationBarFragment;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/**
* A controller to handle external navigation bars
*/
+@Singleton
public class DisplayNavigationBarController implements DisplayListener {
private static final String TAG = DisplayNavigationBarController.class.getName();
@@ -46,7 +53,9 @@
/** A displayId - nav bar mapping */
private SparseArray<NavigationBarFragment> mExternalNavigationBarMap = new SparseArray<>();
- public DisplayNavigationBarController(Context context, Handler handler) {
+ @Inject
+ public DisplayNavigationBarController(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler handler) {
mContext = context;
mHandler = handler;
mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 7d1b640..158430f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,10 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
@@ -35,7 +32,6 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.text.format.Formatter;
@@ -152,10 +148,7 @@
private void registerCallbacks(KeyguardUpdateMonitor monitor) {
monitor.registerCallback(getKeyguardCallback());
- mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
- new IntentFilter(Intent.ACTION_TIME_TICK), null,
- Dependency.get(Dependency.TIME_TICK_HANDLER));
-
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mTickReceiver);
Dependency.get(StatusBarStateController.class).addCallback(this);
}
@@ -166,7 +159,7 @@
* //TODO: This can probably be converted to a fragment and not have to be manually recreated
*/
public void destroy() {
- mContext.unregisterReceiver(mTickReceiver);
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mTickReceiver);
Dependency.get(StatusBarStateController.class).removeCallback(this);
}
@@ -477,16 +470,15 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
}
- private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandler.post(() -> {
- if (mVisible) {
- updateIndication(false);
+ private final KeyguardUpdateMonitorCallback mTickReceiver =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onTimeChanged() {
+ if (mVisible) {
+ updateIndication(false /* animate */);
+ }
}
- });
- }
- };
+ };
private final Handler mHandler = new Handler() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f045548..1a93b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -25,11 +25,10 @@
import android.app.Notification;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -47,6 +46,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
@@ -61,10 +61,14 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
* notification, which this class keeps track of.
*/
+@Singleton
public class NotificationMediaManager implements Dumpable {
private static final String TAG = "NotificationMediaManager";
public static final boolean DEBUG_MEDIA = false;
@@ -90,14 +94,11 @@
@Nullable
private LockscreenWallpaper mLockscreenWallpaper;
- protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- protected final PorterDuffXfermode mSrcOverXferMode =
- new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
-
private final Handler mHandler = Dependency.get(MAIN_HANDLER);
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
+ private final ArrayList<MediaListener> mMediaListeners;
protected NotificationPresenter mPresenter;
private MediaController mMediaController;
@@ -118,7 +119,7 @@
if (state != null) {
if (!isPlaybackActive(state.getState())) {
clearCurrentMediaNotification();
- mPresenter.updateMediaMetaData(true, true);
+ dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
}
}
@@ -130,7 +131,7 @@
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
mMediaMetadata = metadata;
- mPresenter.updateMediaMetaData(true, true);
+ dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
};
@@ -157,8 +158,10 @@
return mEntryManager;
}
+ @Inject
public NotificationMediaManager(Context context) {
mContext = context;
+ mMediaListeners = new ArrayList<>();
mMediaSessionManager
= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
// TODO: use MediaSessionManager.SessionListener to hook us up to future updates
@@ -172,7 +175,7 @@
public void onNotificationRemoved(String key) {
if (key.equals(mMediaNotificationKey)) {
clearCurrentMediaNotification();
- mPresenter.updateMediaMetaData(true, true);
+ dispatchUpdateMediaMetaData(true /* changed */, true /* allowEnterAnimation */);
}
}
@@ -184,20 +187,44 @@
return mMediaMetadata;
}
+ public Icon getMediaIcon() {
+ if (mMediaNotificationKey == null) {
+ return null;
+ }
+ NotificationEntryManager manager = getEntryManager();
+ synchronized (manager.getNotificationData()) {
+ Entry entry = manager.getNotificationData().get(mMediaNotificationKey);
+ if (entry == null || entry.expandedIcon == null) {
+ return null;
+ }
+
+ return entry.expandedIcon.getSourceIcon();
+ }
+ }
+
+ public void addCallback(MediaListener callback) {
+ mMediaListeners.add(callback);
+ callback.onMetadataChanged(mMediaMetadata);
+ }
+
+ public void removeCallback(MediaListener callback) {
+ mMediaListeners.remove(callback);
+ }
+
public void findAndUpdateMediaNotifications() {
boolean metaDataChanged = false;
NotificationEntryManager manager = getEntryManager();
synchronized (manager.getNotificationData()) {
- ArrayList<NotificationData.Entry> activeNotifications = manager
+ ArrayList<Entry> activeNotifications = manager
.getNotificationData().getActiveNotifications();
final int N = activeNotifications.size();
// Promote the media notification with a controller in 'playing' state, if any.
- NotificationData.Entry mediaNotification = null;
+ Entry mediaNotification = null;
MediaController controller = null;
for (int i = 0; i < N; i++) {
- final NotificationData.Entry entry = activeNotifications.get(i);
+ final Entry entry = activeNotifications.get(i);
if (entry.isMediaNotification()) {
final MediaSession.Token token =
@@ -237,7 +264,7 @@
final String pkg = aController.getPackageName();
for (int i = 0; i < N; i++) {
- final NotificationData.Entry entry = activeNotifications.get(i);
+ final Entry entry = activeNotifications.get(i);
if (entry.notification.getPackageName().equals(pkg)) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: found controller matching "
@@ -281,9 +308,7 @@
getEntryManager().updateNotifications();
}
- if (mPresenter != null) {
- mPresenter.updateMediaMetaData(metaDataChanged, true);
- }
+ dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
}
public void clearCurrentMediaNotification() {
@@ -291,6 +316,16 @@
clearCurrentMediaNotificationSession();
}
+ private void dispatchUpdateMediaMetaData(boolean changed, boolean allowEnterAnimation) {
+ if (mPresenter != null) {
+ mPresenter.updateMediaMetaData(changed, allowEnterAnimation);
+ }
+ ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).onMetadataChanged(mMediaMetadata);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mMediaSessionManager=");
@@ -547,4 +582,8 @@
mBackdropFront.setImageDrawable(null);
}
};
+
+ public interface MediaListener {
+ void onMetadataChanged(MediaMetadata metadata);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index a5c0a2d..c945afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,7 @@
*/
package com.android.systemui.statusbar;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -28,7 +28,7 @@
*/
public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
ActivatableNotificationView.OnActivatedListener,
- NotificationEntryManager.Callback {
+ NotificationRowBinder.BindRowCallback {
/**
* Returns true if the presenter is not visible. For example, it may not be necessary to do
* animations if this returns true.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9391737..d1556fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -59,12 +59,16 @@
import java.util.ArrayList;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Class for handling remote input state over a set of notifications. This class handles things
* like keeping notifications temporarily that were cancelled as a response to a remote input
* interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
* and handling clicks on remote views.
*/
+@Singleton
public class NotificationRemoteInputManager implements Dumpable {
public static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
@@ -229,6 +233,7 @@
return mShadeController;
}
+ @Inject
public NotificationRemoteInputManager(Context context) {
mContext = context;
mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 0702f1b..017a9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -25,7 +25,6 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -41,6 +40,11 @@
import java.util.List;
import java.util.Stack;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
/**
* NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
* on their group structure. For example, if a notification becomes bundled with another,
@@ -48,6 +52,7 @@
* tell NotificationListContainer which notifications to display, and inform it of changes to those
* notifications that might affect their display.
*/
+@Singleton
public class NotificationViewHierarchyManager {
private static final String TAG = "NotificationViewHierarchyManager";
@@ -56,20 +61,15 @@
mTmpChildOrderMap = new HashMap<>();
// Dependencies:
- protected final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- protected final NotificationGroupManager mGroupManager =
- Dependency.get(NotificationGroupManager.class);
- protected final VisualStabilityManager mVisualStabilityManager =
- Dependency.get(VisualStabilityManager.class);
- private final StatusBarStateController mStatusBarStateController =
- Dependency.get(StatusBarStateController.class);
- private final NotificationEntryManager mEntryManager =
- Dependency.get(NotificationEntryManager.class);
- private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ protected final NotificationLockscreenUserManager mLockscreenUserManager;
+ protected final NotificationGroupManager mGroupManager;
+ protected final VisualStabilityManager mVisualStabilityManager;
+ private final StatusBarStateController mStatusBarStateController;
+ private final NotificationEntryManager mEntryManager;
+ private final BubbleController mBubbleController;
// Lazy
- private ShadeController mShadeController;
+ private final Lazy<ShadeController> mShadeController;
/**
* {@code true} if notifications not part of a group should by default be rendered in their
@@ -116,20 +116,27 @@
}
}
- private ShadeController getShadeController() {
- if (mShadeController == null) {
- mShadeController = Dependency.get(ShadeController.class);
- }
- return mShadeController;
- }
-
- public NotificationViewHierarchyManager(Context context) {
+ @Inject
+ public NotificationViewHierarchyManager(Context context,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationGroupManager groupManager,
+ VisualStabilityManager visualStabilityManager,
+ StatusBarStateController statusBarStateController,
+ NotificationEntryManager notificationEntryManager,
+ BubbleController bubbleController,
+ Lazy<ShadeController> shadeController) {
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mGroupManager = groupManager;
+ mVisualStabilityManager = visualStabilityManager;
+ mStatusBarStateController = statusBarStateController;
+ mEntryManager = notificationEntryManager;
+ mBubbleController = bubbleController;
+ mShadeController = shadeController;
Resources res = context.getResources();
mAlwaysExpandNonGroupedNotification =
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
- mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
- Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -392,7 +399,7 @@
&& !row.isLowPriority()));
}
- entry.getRow().setOnAmbient(getShadeController().isDozing());
+ entry.getRow().setOnAmbient(mShadeController.get().isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.isRowRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index e31f90d5..6f1548d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -27,10 +27,14 @@
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles when smart replies are added to a notification
* and clicked upon.
*/
+@Singleton
public class SmartReplyController {
private IStatusBarService mBarService;
private Set<String> mSendingKeys = new ArraySet<>();
@@ -38,7 +42,7 @@
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
-
+ @Inject
public SmartReplyController() {
mBarService = Dependency.get(IStatusBarService.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 3f84416..087b655 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -35,9 +35,13 @@
import java.util.ArrayList;
import java.util.Comparator;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Tracks and reports on {@link StatusBarState}.
*/
+@Singleton
public class StatusBarStateController implements CallbackController<StateListener> {
private static final String TAG = "SbStateController";
@@ -101,6 +105,10 @@
public static final int RANK_STACK_SCROLLER = 2;
public static final int RANK_SHELF = 3;
+ @Inject
+ public StatusBarStateController() {
+ }
+
public int getState() {
return mState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 6560f8f..442416f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -26,6 +26,12 @@
import android.os.Vibrator;
import android.provider.Settings;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class VibratorHelper {
private final Vibrator mVibrator;
@@ -44,6 +50,9 @@
}
};
+ /**
+ */
+ @Inject
public VibratorHelper(Context context) {
mContext = context;
mVibrator = context.getSystemService(Vibrator.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f899863..e1b231b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -28,12 +28,12 @@
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.shared.system.SurfaceControlCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -267,8 +267,8 @@
Matrix m = new Matrix();
m.postTranslate(0, (float) (mParams.top - app.position.y));
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
- SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash),
- 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius);
+ SurfaceParams params = new SurfaceParams(app.leash, 1f /* alpha */, m, mWindowCrop,
+ app.prefixOrderIndex, mCornerRadius);
mSyncRtTransactionApplier.scheduleApply(params);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
deleted file mode 100644
index 13e991b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.notification;
-
-/**
- * Listener interface for when NotificationEntryManager needs to tell
- * NotificationGroupAlertTransferHelper things. Will eventually grow to be a general-purpose
- * listening interface for the NotificationEntryManager.
- */
-public interface AlertTransferListener {
- /**
- * Called when a new notification is posted. At this point, the notification is "pending": its
- * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
- */
- void onPendingEntryAdded(NotificationData.Entry entry);
-
- /**
- * Called when an existing notification's views are reinflated (usually due to an update being
- * posted to that notification).
- */
- void onEntryReinflated(NotificationData.Entry entry);
-
- /**
- * Called when a notification has been removed (either because the user swiped it away or
- * because the developer retracted it).
- */
- void onEntryRemoved(NotificationData.Entry entry);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index ae9f323..433a994 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -27,20 +27,15 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import android.Manifest;
import android.annotation.NonNull;
-import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -58,17 +53,13 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.PrintWriter;
@@ -83,14 +74,13 @@
*/
public class NotificationData {
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
/**
* These dependencies are late init-ed
*/
private KeyguardEnvironment mEnvironment;
- private ShadeController mShadeController;
private NotificationMediaManager mMediaManager;
- private ForegroundServiceController mFsc;
- private NotificationLockscreenUserManager mUserManager;
private HeadsUpManager mHeadsUpManager;
@@ -105,6 +95,7 @@
public NotificationChannel channel;
public long lastAudiblyAlertedMs;
public boolean noisy;
+ public boolean ambient;
public int importance;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
@@ -119,6 +110,9 @@
@NonNull
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
+ @VisibleForTesting
+ public int suppressedVisualEffects;
+ public boolean suspended;
private Entry parent; // our parent (if we're in a group)
private ArrayList<Entry> children = new ArrayList<Entry>();
@@ -174,6 +168,7 @@
channel = ranking.getChannel();
lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
importance = ranking.getImportance();
+ ambient = ranking.isAmbient();
snoozeCriteria = ranking.getSnoozeCriteria();
userSentiment = ranking.getUserSentiment();
systemGeneratedSmartActions = ranking.getSmartActions() == null
@@ -181,6 +176,8 @@
smartReplies = ranking.getSmartReplies() == null
? new CharSequence[0]
: ranking.getSmartReplies().toArray(new CharSequence[0]);
+ suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+ suspended = ranking.isSuspended();
}
public void setInterruption() {
@@ -623,6 +620,71 @@
if (row == null) return true;
return row.canViewBeDismissed();
}
+
+ boolean isExemptFromDndVisualSuppression() {
+ if (isNotificationBlockedByPolicy(notification.getNotification())) {
+ return false;
+ }
+
+ if ((notification.getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return true;
+ }
+ if (notification.getNotification().isMediaNotification()) {
+ return true;
+ }
+ if (mIsSystemNotification != null && mIsSystemNotification) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldSuppressVisualEffect(int effect) {
+ if (isExemptFromDndVisualSuppression()) {
+ return false;
+ }
+ return (suppressedVisualEffects & effect) != 0;
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressFullScreenIntent() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressPeek() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressStatusBar() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressAmbient() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressNotificationList() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -704,13 +766,6 @@
return mEnvironment;
}
- private ShadeController getShadeController() {
- if (mShadeController == null) {
- mShadeController = Dependency.get(ShadeController.class);
- }
- return mShadeController;
- }
-
private NotificationMediaManager getMediaManager() {
if (mMediaManager == null) {
mMediaManager = Dependency.get(NotificationMediaManager.class);
@@ -718,20 +773,6 @@
return mMediaManager;
}
- private ForegroundServiceController getFsc() {
- if (mFsc == null) {
- mFsc = Dependency.get(ForegroundServiceController.class);
- }
- return mFsc;
- }
-
- private NotificationLockscreenUserManager getUserManager() {
- if (mUserManager == null) {
- mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
- }
- return mUserManager;
- }
-
/**
* Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
*
@@ -776,7 +817,7 @@
}
public Entry remove(String key, RankingMap ranking) {
- Entry removed = null;
+ Entry removed;
synchronized (mEntries) {
removed = mEntries.remove(key);
}
@@ -847,62 +888,12 @@
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- public boolean shouldSuppressFullScreenIntent(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
- }
-
- public boolean shouldSuppressPeek(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_PEEK);
- }
-
- public boolean shouldSuppressStatusBar(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_STATUS_BAR);
- }
-
- public boolean shouldSuppressAmbient(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_AMBIENT);
- }
-
- public boolean shouldSuppressNotificationList(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
- }
-
- private boolean shouldSuppressVisualEffect(Entry entry, int effect) {
- if (isExemptFromDndVisualSuppression(entry)) {
- return false;
- }
- String key = entry.key;
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
- }
- return false;
- }
-
- protected boolean isExemptFromDndVisualSuppression(Entry entry) {
- if (isNotificationBlockedByPolicy(entry.notification.getNotification())) {
- return false;
- }
-
- if ((entry.notification.getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return true;
- }
- if (entry.notification.getNotification().isMediaNotification()) {
- return true;
- }
- if (entry.mIsSystemNotification != null && entry.mIsSystemNotification) {
- return true;
- }
- return false;
- }
-
/**
* Categories that are explicitly called out on DND settings screens are always blocked, if
* DND has flagged them, even if they are foreground or system notifications that might
* otherwise visually bypass DND.
*/
- protected boolean isNotificationBlockedByPolicy(Notification n) {
+ private static boolean isNotificationBlockedByPolicy(Notification n) {
if (isCategory(CATEGORY_CALL, n)
|| isCategory(CATEGORY_MESSAGE, n)
|| isCategory(CATEGORY_ALARM, n)
@@ -913,7 +904,7 @@
return false;
}
- private boolean isCategory(String category, Notification n) {
+ private static boolean isCategory(String category, Notification n) {
return Objects.equals(n.category, category);
}
@@ -1011,7 +1002,7 @@
for (int i = 0; i < N; i++) {
Entry entry = mEntries.valueAt(i);
- if (shouldFilterOut(entry)) {
+ if (mNotificationFilter.shouldFilterOut(entry)) {
continue;
}
@@ -1022,87 +1013,6 @@
Collections.sort(mSortedAndFiltered, mRankingComparator);
}
- /**
- * @return true if this notification should NOT be shown right now
- */
- public boolean shouldFilterOut(Entry entry) {
- final StatusBarNotification sbn = entry.notification;
- if (!(getEnvironment().isDeviceProvisioned() ||
- showNotificationEvenIfUnprovisioned(sbn))) {
- return true;
- }
-
- if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
- return true;
- }
-
- if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) &&
- (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
- || getUserManager().shouldHideNotifications(sbn.getUserId())
- || getUserManager().shouldHideNotifications(sbn.getKey()))) {
- return true;
- }
-
- if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) {
- return true;
- }
-
- if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) {
- return true;
- }
-
- if (shouldHide(sbn.getKey())) {
- return true;
- }
-
- if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
- && mGroupManager.isChildInGroupWithSummary(sbn)) {
- return true;
- }
-
- if (getFsc().isDungeonNotification(sbn)
- && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
- // this is a foreground-service disclosure for a user that does not need to show one
- return true;
- }
- if (getFsc().isSystemAlertNotification(sbn)) {
- final String[] apps = sbn.getNotification().extras.getStringArray(
- Notification.EXTRA_FOREGROUND_APPS);
- if (apps != null && apps.length >= 1) {
- if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Q: What kinds of notifications should show during setup?
- // A: Almost none! Only things coming from packages with permission
- // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
- // as relevant for setup (see below).
- public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
- return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
- }
-
- @VisibleForTesting
- static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
- StatusBarNotification sbn) {
- return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
- sbn.getUid()) == PackageManager.PERMISSION_GRANTED
- && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
- }
-
- private static int checkUidPermission(IPackageManager packageManager, String permission,
- int uid) {
- try {
- return packageManager.checkUidPermission(permission, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
public void dump(PrintWriter pw, String indent) {
int N = mSortedAndFiltered.size();
pw.print(indent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
new file mode 100644
index 0000000..361ae8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -0,0 +1,75 @@
+/*
+ * 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.notification;
+
+import android.service.notification.StatusBarNotification;
+
+/**
+ * Listener interface for changes sent by NotificationEntryManager.
+ */
+public interface NotificationEntryListener {
+ /**
+ * Called when a new notification is posted. At this point, the notification is "pending": its
+ * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
+ */
+ default void onPendingEntryAdded(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a new entry is created.
+ */
+ default void onNotificationAdded(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification was updated.
+ */
+ default void onNotificationUpdated(StatusBarNotification notification) {
+ }
+
+ /**
+ * Called when an existing notification's views are reinflated (usually due to an update being
+ * posted to that notification).
+ */
+ default void onEntryReinflated(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification has been removed (either because the user swiped it away or
+ * because the developer retracted it).
+ *
+ * TODO: combine this with onNotificationRemoved().
+ */
+ default void onEntryRemoved(NotificationData.Entry entry) {
+ }
+
+ /**
+ * Called when a notification was removed.
+ *
+ * @param key key of notification that was removed
+ * @param old StatusBarNotification of the notification before it was removed
+ */
+ default void onNotificationRemoved(String key, StatusBarNotification old) {
+ }
+
+ /**
+ * Removes a notification immediately.
+ *
+ * TODO: combine this with onNotificationRemoved().
+ */
+ default void onPerformRemoveNotification(StatusBarNotification statusBarNotification) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index d2a5864..98ddd6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,75 +15,50 @@
*/
package com.android.systemui.statusbar.notification;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Log;
-import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.EventLogTags;
import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
@@ -100,17 +75,17 @@
* It also handles tasks such as their inflation and their interaction with other
* Notification.*Manager objects.
*/
-public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
- ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
- VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
+public class NotificationEntryManager implements
+ Dumpable,
+ NotificationInflater.InflationCallback,
+ NotificationUpdateHandler,
+ VisualStabilityManager.Callback,
+ BubbleController.BubbleDismissListener {
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean ENABLE_HEADS_UP = true;
- private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
- private final NotificationMessagingUtil mMessagingUtil;
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
@@ -118,46 +93,40 @@
Dependency.get(NotificationGroupManager.class);
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final DeviceProvisionedController mDeviceProvisionedController =
Dependency.get(DeviceProvisionedController.class);
private final VisualStabilityManager mVisualStabilityManager =
Dependency.get(VisualStabilityManager.class);
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
private final ForegroundServiceController mForegroundServiceController =
Dependency.get(ForegroundServiceController.class);
private final AmbientPulseManager mAmbientPulseManager =
Dependency.get(AmbientPulseManager.class);
private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
// Lazily retrieved dependencies
private NotificationRemoteInputManager mRemoteInputManager;
private NotificationMediaManager mMediaManager;
private NotificationListener mNotificationListener;
private ShadeController mShadeController;
+ private NotificationRowBinder mNotificationRowBinder;
private final Handler mDeferredNotificationViewUpdateHandler;
private Runnable mUpdateNotificationViewsCallback;
- protected IDreamManager mDreamManager;
protected IStatusBarService mBarService;
private NotificationPresenter mPresenter;
- private Callback mCallback;
+ private NotificationEntryListener mCallback;
protected PowerManager mPowerManager;
private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
- private ContentObserver mHeadsUpObserver;
- protected boolean mUseHeadsUp = false;
- private boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
@VisibleForTesting
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
- private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
- private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
- @Nullable private AlertTransferListener mAlertTransferListener;
- @Nullable private NotificationClicker mNotificationClicker;
+ private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
private final DeviceProvisionedController.DeviceProvisionedListener
mDeviceProvisionedListener =
@@ -168,11 +137,6 @@
}
};
- public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
- mDisableNotificationAlerts = disableNotificationAlerts;
- mHeadsUpObserver.onChange(true);
- }
-
public void destroy() {
mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
}
@@ -188,8 +152,6 @@
pw.println(entry.notification);
}
}
- pw.print(" mUseHeadsUp=");
- pw.println(mUseHeadsUp);
}
public NotificationEntryManager(Context context) {
@@ -197,20 +159,14 @@
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
- mMessagingUtil = new NotificationMessagingUtil(context);
mBubbleController.setDismissListener(this /* bubbleEventListener */);
mNotificationData = new NotificationData();
mDeferredNotificationViewUpdateHandler = new Handler();
}
- public void setAlertTransferListener(AlertTransferListener listener) {
- mAlertTransferListener = listener;
- }
-
- public void setNotificationClicker(NotificationClicker clicker) {
- mNotificationClicker = clicker;
+ /** Adds a {@link NotificationEntryListener}. */
+ public void addNotificationEntryListener(NotificationEntryListener listener) {
+ mNotificationEntryListeners.add(listener);
}
/**
@@ -244,8 +200,15 @@
return mShadeController;
}
+ private NotificationRowBinder getRowBinder() {
+ if (mNotificationRowBinder == null) {
+ mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
+ }
+ return mNotificationRowBinder;
+ }
+
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationListContainer listContainer, Callback callback,
+ NotificationListContainer listContainer, NotificationEntryListener callback,
HeadsUpManager headsUpManager) {
mPresenter = presenter;
mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
@@ -254,36 +217,6 @@
mNotificationData.setHeadsUpManager(mHeadsUpManager);
mListContainer = listContainer;
- mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
- @Override
- public void onChange(boolean selfChange) {
- boolean wasUsing = mUseHeadsUp;
- mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
- && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- Settings.Global.HEADS_UP_OFF);
- Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
- if (wasUsing != mUseHeadsUp) {
- if (!mUseHeadsUp) {
- Log.d(TAG,
- "dismissing any existing heads up notification on disable event");
- mHeadsUpManager.releaseAllImmediately();
- }
- }
- }
- };
-
- if (ENABLE_HEADS_UP) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
- true,
- mHeadsUpObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
- mHeadsUpObserver);
- }
-
mNotificationLifetimeExtenders.add(mHeadsUpManager);
mNotificationLifetimeExtenders.add(mAmbientPulseManager);
mNotificationLifetimeExtenders.add(mGutsManager);
@@ -294,9 +227,6 @@
}
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-
- mHeadsUpObserver.onChange(true); // set up
- mOnAppOpsClickListener = mGutsManager::openGuts;
}
public NotificationData getNotificationData() {
@@ -312,18 +242,7 @@
}
public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
- return mGutsManager::openGuts;
- }
-
- @Override
- public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
- mUiOffloadThread.submit(() -> {
- try {
- mBarService.onNotificationExpansionChanged(key, userAction, expanded);
- } catch (RemoteException e) {
- // Ignore.
- }
- });
+ return getRowBinder().getNotificationLongClicker();
}
@Override
@@ -331,72 +250,6 @@
updateNotifications();
}
- private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
- if (mPresenter.isDeviceInVrMode()) {
- return true;
- }
-
- return mNotificationData.shouldSuppressFullScreenIntent(entry);
- }
-
- private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
- entry.notification.getUser().getIdentifier());
-
- final StatusBarNotification sbn = entry.notification;
- if (entry.rowExists()) {
- entry.reset();
- updateNotification(entry, pmUser, sbn, entry.getRow());
- } else {
- new RowInflaterTask().inflate(mContext, parent, entry,
- row -> {
- bindRow(entry, pmUser, sbn, row);
- updateNotification(entry, pmUser, sbn, row);
- });
- }
- }
-
- private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row) {
- row.setExpansionLogger(this, entry.notification.getKey());
- row.setGroupManager(mGroupManager);
- row.setHeadsUpManager(mHeadsUpManager);
- row.setOnExpandClickListener(mPresenter);
- row.setInflationCallback(this);
- row.setLongPressListener(getNotificationLongClicker());
- mListContainer.bindRow(row);
- getRemoteInputManager().bindRow(row);
-
- // Get the app name.
- // Note that Notification.Builder#bindHeaderAppName has similar logic
- // but since this field is used in the guts, it must be accurate.
- // Therefore we will only show the application label, or, failing that, the
- // package name. No substitutions.
- final String pkg = sbn.getPackageName();
- String appname = pkg;
- try {
- final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- if (info != null) {
- appname = String.valueOf(pmUser.getApplicationLabel(info));
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Do nothing
- }
- row.setAppName(appname);
- row.setOnDismissRunnable(() ->
- performRemoveNotification(row.getStatusBarNotification()));
- row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- if (ENABLE_REMOTE_INPUT) {
- row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
- }
-
- row.setAppOpsOnClickListener(mOnAppOpsClickListener);
-
- mCallback.onBindRow(entry, pmUser, sbn, row);
- }
-
public void performRemoveNotification(StatusBarNotification n) {
final int rank = mNotificationData.getRank(n.getKey());
final int count = mNotificationData.getActiveNotifications().size();
@@ -508,7 +361,7 @@
if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
- if (shouldHeadsUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
mHeadsUpManager.showNotification(entry);
// Mark as seen immediately
setNotificationShown(entry.notification);
@@ -517,7 +370,7 @@
}
}
if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
- if (shouldPulse(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
mAmbientPulseManager.showNotification(entry);
} else {
entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
@@ -541,8 +394,8 @@
mVisualStabilityManager.onLowPriorityUpdated(entry);
mPresenter.updateNotificationViews();
}
- if (mAlertTransferListener != null) {
- mAlertTransferListener.onEntryReinflated(entry);
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onEntryReinflated(entry);
}
}
}
@@ -559,8 +412,10 @@
final NotificationData.Entry entry = mNotificationData.get(key);
abortExistingInflation(key);
- if (mAlertTransferListener != null && entry != null) {
- mAlertTransferListener.onEntryRemoved(entry);
+ if (entry != null) {
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onEntryRemoved(entry);
+ }
}
// Attempt to remove notifications from their alert managers (heads up, ambient pulse).
@@ -687,56 +542,11 @@
}
}
- //TODO: This method associates a row with an entry, but eventually needs to not do that
- protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row) {
- boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
- boolean isUpdate = mNotificationData.get(entry.key) != null;
- boolean wasLowPriority = row.isLowPriority();
- row.setIsLowPriority(isLowPriority);
- row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
- // bind the click event to the content area
- checkNotNull(mNotificationClicker).register(row, sbn);
-
- // Extract target SDK version.
- try {
- ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
- entry.targetSdk = info.targetSdkVersion;
- } catch (PackageManager.NameNotFoundException ex) {
- Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
- }
- row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
- && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
-
- entry.setRow(row);
- row.setOnActivatedListener(mPresenter);
-
- boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
- mNotificationData.getImportance(sbn.getKey()));
- boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
- && !mPresenter.isPresenterFullyCollapsed();
- row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
- row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
- row.setEntry(entry);
-
- if (shouldHeadsUp(entry)) {
- row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
- }
- if (shouldPulse(entry)) {
- row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
- }
- row.setNeedsRedaction(
- Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
- row.inflateViews();
- }
-
- private NotificationData.Entry createNotificationViews(
+ private NotificationData.Entry createNotificationEntry(
StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
throws InflationException {
if (DEBUG) {
- Log.d(TAG, "createNotificationViews(notification=" + sbn + " " + ranking);
+ Log.d(TAG, "createNotificationEntry(notification=" + sbn + " " + ranking);
}
NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
@@ -747,7 +557,8 @@
Dependency.get(LeakDetector.class).trackInstance(entry);
entry.createIcons(mContext, sbn);
// Construct the expanded view.
- inflateViews(entry, mListContainer.getViewParentForNotification(entry));
+ getRowBinder().inflateViews(entry, () -> performRemoveNotification(sbn),
+ mNotificationData.get(entry.key) != null);
return entry;
}
@@ -761,51 +572,15 @@
mNotificationData.updateRanking(rankingMap);
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
- NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
- boolean isHeadsUped = shouldHeadsUp(shadeEntry);
- if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
- if (shouldSuppressFullScreenIntent(shadeEntry)) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
- }
- } else if (mNotificationData.getImportance(key)
- < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: not important enough: "
- + key);
- }
- } else {
- // Stop screensaver if the notification has a fullscreen intent.
- // (like an incoming phone call)
- Dependency.get(UiOffloadThread.class).submit(() -> {
- try {
- mDreamManager.awaken();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- });
-
- // not immersive & a fullscreen alert should be shown
- if (DEBUG)
- Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
- try {
- EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
- key);
- notification.getNotification().fullScreenIntent.send();
- shadeEntry.notifyFullScreenIntentLaunched();
- mMetricsLogger.count("note_fullscreen", 1);
- } catch (PendingIntent.CanceledException e) {
- }
- }
- }
+ NotificationData.Entry entry = createNotificationEntry(notification, ranking);
abortExistingInflation(key);
mForegroundServiceController.addNotification(notification,
mNotificationData.getImportance(key));
- mPendingNotifications.put(key, shadeEntry);
- if (mAlertTransferListener != null) {
- mAlertTransferListener.onPendingEntryAdded(shadeEntry);
+ mPendingNotifications.put(key, entry);
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onPendingEntryAdded(entry);
}
}
@@ -870,16 +645,19 @@
mGroupManager.onEntryUpdated(entry, oldNotification);
entry.updateIcons(mContext, notification);
- inflateViews(entry, mListContainer.getViewParentForNotification(entry));
+ getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
+ mNotificationData.get(entry.key) != null);
mForegroundServiceController.updateNotification(notification,
mNotificationData.getImportance(key));
boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
if (getShadeController().isDozing()) {
- updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldPulse(entry),
+ alertAgain, mAmbientPulseManager);
} else {
- updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldHeadsUp(entry),
+ alertAgain, mHeadsUpManager);
}
updateNotifications();
@@ -938,26 +716,12 @@
// By comparing the old and new UI adjustments, reinflate the view accordingly.
for (NotificationData.Entry entry : entries) {
- NotificationUiAdjustment newAdjustment =
- NotificationUiAdjustment.extractFromNotificationEntry(entry);
-
- if (NotificationUiAdjustment.needReinflate(
- oldAdjustments.get(entry.key), newAdjustment)) {
- if (entry.rowExists()) {
- entry.reset();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
- entry.notification.getUser().getIdentifier());
- updateNotification(entry, pmUser, entry.notification, entry.getRow());
- } else {
- // Once the RowInflaterTask is done, it will pick up the updated entry, so
- // no-op here.
- }
- } else if (oldImportances.containsKey(entry.key)
- && entry.importance != oldImportances.get(entry.key)) {
- if (entry.rowExists()) {
- entry.getRow().onNotificationRankingUpdated();
- }
- }
+ getRowBinder().onNotificationRankingUpdated(
+ entry,
+ oldImportances.get(entry.key),
+ oldAdjustments.get(entry.key),
+ NotificationUiAdjustment.extractFromNotificationEntry(entry),
+ mNotificationData.get(entry.key) != null);
}
updateNotifications();
@@ -975,182 +739,6 @@
}
}
- public void setStatusBarStateListener(
- NotificationViewHierarchyManager.StatusBarStateListener listener) {
- mStatusBarStateListener = listener;
- }
-
- /**
- * Whether the notification should peek in from the top and alert the user.
- *
- * @param entry the entry to check
- * @return true if the entry should heads up, false otherwise
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
- }
- return false;
- }
-
- // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
- // message bubble from the bubble to go through heads up path
- boolean inShade = mStatusBarStateListener != null
- && mStatusBarStateListener.getCurrentState() == SHADE;
- if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: no huns or vr mode");
- }
- return false;
- }
-
- boolean isDreaming = false;
- try {
- isDreaming = mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query dream manager.", e);
- }
- boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
-
- if (!inUse) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressPeek(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
- }
- return false;
- }
-
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
- }
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mCallback.canHeadsUp(entry, sbn)) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Whether or not the notification should "pulse" on the user's display when the phone is
- * dozing. This displays the ambient view of the notification.
- *
- * @param entry the entry to check
- * @return true if the entry should ambient pulse, false otherwise
- */
- private boolean shouldPulse(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (!getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
- }
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressAmbient(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey())
- < NotificationManager.IMPORTANCE_DEFAULT) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
- }
- return false;
- }
-
- Bundle extras = sbn.getNotification().extras;
- CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
- CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
- if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
- }
- return false;
- }
-
- return true;
- }
-
- /**
- * Common checks between heads up alerting and ambient pulse alerting. See
- * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
- * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that
- * fail any of these checks should not alert at all.
- *
- * @param entry the entry to check
- * @return true if these checks pass, false if the notification should not alert
- */
- protected boolean canAlertCommon(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (mNotificationData.shouldFilterOut(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
- }
- return false;
- }
-
- // Don't alert notifications that are suppressed due to group alert behavior
- if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: suppressed due to group alert behavior");
- }
- return false;
- }
-
- return true;
- }
-
private void setNotificationShown(StatusBarNotification n) {
setNotificationsShown(new String[]{n.getKey()});
}
@@ -1163,10 +751,6 @@
}
}
- private boolean isSnoozedPackage(StatusBarNotification sbn) {
- return mHeadsUpManager.isSnoozed(sbn.getPackageName());
- }
-
/**
* Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
* method.
@@ -1200,59 +784,4 @@
public Iterable<NotificationData.Entry> getPendingNotificationsIterator() {
return mPendingNotifications.values();
}
-
- /**
- * Callback for NotificationEntryManager.
- */
- public interface Callback {
-
- /**
- * Called when a new entry is created.
- *
- * @param shadeEntry entry that was created
- */
- void onNotificationAdded(NotificationData.Entry shadeEntry);
-
- /**
- * Called when a notification was updated.
- *
- * @param notification notification that was updated
- */
- void onNotificationUpdated(StatusBarNotification notification);
-
- /**
- * Called when a notification was removed.
- *
- * @param key key of notification that was removed
- * @param old StatusBarNotification of the notification before it was removed
- */
- void onNotificationRemoved(String key, StatusBarNotification old);
-
- /**
- * Called when a new notification and row is created.
- *
- * @param entry entry for the notification
- * @param pmUser package manager for user
- * @param sbn notification
- * @param row row for the notification
- */
- void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row);
-
- /**
- * Removes a notification immediately.
- *
- * @param statusBarNotification notification that is being removed
- */
- void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
-
- /**
- * Returns true if NotificationEntryManager can heads up this notification.
- *
- * @param entry entry of the notification that might be heads upped
- * @param sbn notification that might be heads upped
- * @return true if the notification can be heads upped
- */
- boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
new file mode 100644
index 0000000..5e99c38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -0,0 +1,162 @@
+/*
+ * 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.notification;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Component which manages the various reasons a notification might be filtered out. */
+@Singleton
+public class NotificationFilter {
+
+ private final NotificationGroupManager mGroupManager = Dependency.get(
+ NotificationGroupManager.class);
+
+ private NotificationData.KeyguardEnvironment mEnvironment;
+ private ShadeController mShadeController;
+ private ForegroundServiceController mFsc;
+ private NotificationLockscreenUserManager mUserManager;
+
+ @Inject
+ public NotificationFilter() {}
+
+ private NotificationData.KeyguardEnvironment getEnvironment() {
+ if (mEnvironment == null) {
+ mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+ }
+ return mEnvironment;
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ private ForegroundServiceController getFsc() {
+ if (mFsc == null) {
+ mFsc = Dependency.get(ForegroundServiceController.class);
+ }
+ return mFsc;
+ }
+
+ private NotificationLockscreenUserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+ }
+ return mUserManager;
+ }
+
+
+ /**
+ * @return true if the provided notification should NOT be shown right now.
+ */
+ public boolean shouldFilterOut(NotificationData.Entry entry) {
+ final StatusBarNotification sbn = entry.notification;
+ if (!(getEnvironment().isDeviceProvisioned()
+ || showNotificationEvenIfUnprovisioned(sbn))) {
+ return true;
+ }
+
+ if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+ return true;
+ }
+
+ if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
+ && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
+ || getUserManager().shouldHideNotifications(sbn.getUserId())
+ || getUserManager().shouldHideNotifications(sbn.getKey()))) {
+ return true;
+ }
+
+ if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) {
+ return true;
+ }
+
+ if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) {
+ return true;
+ }
+
+ if (entry.suspended) {
+ return true;
+ }
+
+ if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.isChildInGroupWithSummary(sbn)) {
+ return true;
+ }
+
+ if (getFsc().isDungeonNotification(sbn)
+ && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
+ // this is a foreground-service disclosure for a user that does not need to show one
+ return true;
+ }
+ if (getFsc().isSystemAlertNotification(sbn)) {
+ final String[] apps = sbn.getNotification().extras.getStringArray(
+ Notification.EXTRA_FOREGROUND_APPS);
+ if (apps != null && apps.length >= 1) {
+ if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Q: What kinds of notifications should show during setup?
+ // A: Almost none! Only things coming from packages with permission
+ // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
+ // as relevant for setup (see below).
+ private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+ return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
+ }
+
+ @VisibleForTesting
+ static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
+ StatusBarNotification sbn) {
+ return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
+ sbn.getUid()) == PackageManager.PERMISSION_GRANTED
+ && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+ }
+
+ private static int checkUidPermission(IPackageManager packageManager, String permission,
+ int uid) {
+ try {
+ return packageManager.checkUidPermission(permission, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..8bd0e9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -0,0 +1,332 @@
+/*
+ * 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.notification;
+
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Provides heads-up and pulsing state for notification entries.
+ */
+public class NotificationInterruptionStateProvider {
+
+ private static final String TAG = "InterruptionStateProvider";
+ private static final boolean DEBUG = false;
+ private static final boolean ENABLE_HEADS_UP = true;
+ private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+ private final StatusBarStateController mStatusBarStateController =
+ Dependency.get(StatusBarStateController.class);
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+ private final Context mContext;
+ private final PowerManager mPowerManager;
+ private final IDreamManager mDreamManager;
+
+ private NotificationPresenter mPresenter;
+ private ShadeController mShadeController;
+ private HeadsUpManager mHeadsUpManager;
+ private HeadsUpSuppressor mHeadsUpSuppressor;
+
+ private ContentObserver mHeadsUpObserver;
+ @VisibleForTesting
+ protected boolean mUseHeadsUp = false;
+ private boolean mDisableNotificationAlerts;
+
+ public NotificationInterruptionStateProvider(Context context) {
+ this(context,
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE),
+ IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE)));
+ }
+
+ @VisibleForTesting
+ protected NotificationInterruptionStateProvider(
+ Context context,
+ PowerManager powerManager,
+ IDreamManager dreamManager) {
+ mContext = context;
+ mPowerManager = powerManager;
+ mDreamManager = dreamManager;
+ }
+
+ /** Sets up late-binding dependencies for this component. */
+ public void setUpWithPresenter(
+ NotificationPresenter notificationPresenter,
+ HeadsUpManager headsUpManager,
+ HeadsUpSuppressor headsUpSuppressor) {
+ mPresenter = notificationPresenter;
+ mHeadsUpManager = headsUpManager;
+ mHeadsUpSuppressor = headsUpSuppressor;
+
+ mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+ @Override
+ public void onChange(boolean selfChange) {
+ boolean wasUsing = mUseHeadsUp;
+ mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
+ && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ Settings.Global.HEADS_UP_OFF);
+ Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+ if (wasUsing != mUseHeadsUp) {
+ if (!mUseHeadsUp) {
+ Log.d(TAG,
+ "dismissing any existing heads up notification on disable event");
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ }
+ }
+ };
+
+ if (ENABLE_HEADS_UP) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+ true,
+ mHeadsUpObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+ mHeadsUpObserver);
+ }
+ mHeadsUpObserver.onChange(true); // set up
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ /**
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
+ // message bubble from the bubble to go through heads up path
+ boolean inShade = mStatusBarStateController.getState() == SHADE;
+ if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: no huns or vr mode");
+ }
+ return false;
+ }
+
+ boolean isDreaming = false;
+ try {
+ isDreaming = mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query dream manager.", e);
+ }
+ boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+
+ if (!inUse) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressPeek()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether or not the notification should "pulse" on the user's display when the phone is
+ * dozing. This displays the ambient view of the notification.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should ambient pulse, false otherwise
+ */
+ public boolean shouldPulse(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (!getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressAmbient()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ Bundle extras = sbn.getNotification().extras;
+ CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
+ CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
+ if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Common checks between heads up alerting and ambient pulse alerting. See
+ * {@link #shouldHeadsUp(NotificationData.Entry)} and
+ * {@link #shouldPulse(NotificationData.Entry)}. Notifications that fail any of these checks
+ * should not alert at all.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ protected boolean canAlertCommon(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (mNotificationFilter.shouldFilterOut(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // Don't alert notifications that are suppressed due to group alert behavior
+ if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isSnoozedPackage(StatusBarNotification sbn) {
+ return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+ }
+
+ /** Sets whether to disable all alerts. */
+ public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+ mDisableNotificationAlerts = disableNotificationAlerts;
+ mHeadsUpObserver.onChange(true);
+ }
+
+ protected NotificationPresenter getPresenter() {
+ return mPresenter;
+ }
+
+ /** A component which can suppress heads-up notifications due to the overall state of the UI. */
+ public interface HeadsUpSuppressor {
+ /**
+ * Returns false if the provided notification is ineligible for heads-up according to this
+ * component.
+ *
+ * @param entry entry of the notification that might be heads upped
+ * @param sbn notification that might be heads upped
+ * @return false if the notification can not be heads upped
+ */
+ boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
new file mode 100644
index 0000000..b241b8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -0,0 +1,290 @@
+/*
+ * 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.notification;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.NotificationMessagingUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Handles inflating and updating views for notifications. */
+@Singleton
+public class NotificationRowBinder {
+
+ private static final String TAG = "NotificationViewManager";
+
+ private final NotificationGroupManager mGroupManager =
+ Dependency.get(NotificationGroupManager.class);
+ private final NotificationGutsManager mGutsManager =
+ Dependency.get(NotificationGutsManager.class);
+ private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+
+ private final Context mContext;
+ private final IStatusBarService mBarService;
+ private final NotificationMessagingUtil mMessagingUtil;
+ private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
+ this::logNotificationExpansion;
+
+ private NotificationRemoteInputManager mRemoteInputManager;
+ private NotificationPresenter mPresenter;
+ private NotificationListContainer mListContainer;
+ private HeadsUpManager mHeadsUpManager;
+ private NotificationInflater.InflationCallback mInflationCallback;
+ private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
+ private BindRowCallback mBindRowCallback;
+ private NotificationClicker mNotificationClicker;
+
+ @Inject
+ public NotificationRowBinder(Context context) {
+ mContext = context;
+ mMessagingUtil = new NotificationMessagingUtil(context);
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ private NotificationRemoteInputManager getRemoteInputManager() {
+ if (mRemoteInputManager == null) {
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+ }
+ return mRemoteInputManager;
+ }
+
+ /**
+ * Sets up late-bound dependencies for this component.
+ */
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ NotificationListContainer listContainer,
+ HeadsUpManager headsUpManager,
+ NotificationInflater.InflationCallback inflationCallback,
+ BindRowCallback bindRowCallback) {
+ mPresenter = presenter;
+ mListContainer = listContainer;
+ mHeadsUpManager = headsUpManager;
+ mInflationCallback = inflationCallback;
+ mBindRowCallback = bindRowCallback;
+ mOnAppOpsClickListener = mGutsManager::openGuts;
+ }
+
+ public void setNotificationClicker(NotificationClicker clicker) {
+ mNotificationClicker = clicker;
+ }
+
+ /**
+ * Inflates the views for the given entry (possibly asynchronously).
+ */
+ public void inflateViews(NotificationData.Entry entry, Runnable onDismissRunnable,
+ boolean isUpdate) {
+ ViewGroup parent = mListContainer.getViewParentForNotification(entry);
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ entry.notification.getUser().getIdentifier());
+
+ final StatusBarNotification sbn = entry.notification;
+ if (entry.rowExists()) {
+ entry.reset();
+ updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate);
+ } else {
+ new RowInflaterTask().inflate(mContext, parent, entry,
+ row -> {
+ bindRow(entry, pmUser, sbn, row, onDismissRunnable);
+ updateNotification(entry, pmUser, sbn, row, isUpdate);
+ });
+ }
+ }
+
+ private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row,
+ Runnable onDismissRunnable) {
+ row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
+ row.setGroupManager(mGroupManager);
+ row.setHeadsUpManager(mHeadsUpManager);
+ row.setOnExpandClickListener(mPresenter);
+ row.setInflationCallback(mInflationCallback);
+ row.setLongPressListener(getNotificationLongClicker());
+ mListContainer.bindRow(row);
+ getRemoteInputManager().bindRow(row);
+
+ // Get the app name.
+ // Note that Notification.Builder#bindHeaderAppName has similar logic
+ // but since this field is used in the guts, it must be accurate.
+ // Therefore we will only show the application label, or, failing that, the
+ // package name. No substitutions.
+ final String pkg = sbn.getPackageName();
+ String appname = pkg;
+ try {
+ final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ if (info != null) {
+ appname = String.valueOf(pmUser.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Do nothing
+ }
+ row.setAppName(appname);
+ row.setOnDismissRunnable(onDismissRunnable);
+ row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ if (ENABLE_REMOTE_INPUT) {
+ row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+ }
+
+ row.setAppOpsOnClickListener(mOnAppOpsClickListener);
+
+ mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
+ }
+
+ /**
+ * Updates the views bound to an entry when the entry's ranking changes, either in-place or by
+ * reinflating them.
+ */
+ public void onNotificationRankingUpdated(
+ NotificationData.Entry entry,
+ @Nullable Integer oldImportance,
+ NotificationUiAdjustment oldAdjustment,
+ NotificationUiAdjustment newAdjustment,
+ boolean isUpdate) {
+ if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
+ if (entry.rowExists()) {
+ entry.reset();
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ mContext,
+ entry.notification.getUser().getIdentifier());
+ updateNotification(entry, pmUser, entry.notification, entry.getRow(), isUpdate);
+ } else {
+ // Once the RowInflaterTask is done, it will pick up the updated entry, so
+ // no-op here.
+ }
+ } else {
+ if (oldImportance != null && entry.importance != oldImportance) {
+ if (entry.rowExists()) {
+ entry.getRow().onNotificationRankingUpdated();
+ }
+ }
+ }
+ }
+
+ //TODO: This method associates a row with an entry, but eventually needs to not do that
+ private void updateNotification(
+ NotificationData.Entry entry,
+ PackageManager pmUser,
+ StatusBarNotification sbn,
+ ExpandableNotificationRow row,
+ boolean isUpdate) {
+ boolean isLowPriority = entry.ambient;
+ boolean wasLowPriority = row.isLowPriority();
+ row.setIsLowPriority(isLowPriority);
+ row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
+ // bind the click event to the content area
+ checkNotNull(mNotificationClicker).register(row, sbn);
+
+ // Extract target SDK version.
+ try {
+ ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
+ entry.targetSdk = info.targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException ex) {
+ Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
+ }
+ row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
+ && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+
+ // TODO: should updates to the entry be happening somewhere else?
+ entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+ entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
+
+ entry.setRow(row);
+ row.setOnActivatedListener(mPresenter);
+
+ boolean useIncreasedCollapsedHeight =
+ mMessagingUtil.isImportantMessaging(sbn, entry.importance);
+ boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
+ && !mPresenter.isPresenterFullyCollapsed();
+ row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+ row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+ row.setEntry(entry);
+
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
+ }
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
+ }
+ row.setNeedsRedaction(
+ Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
+ row.inflateViews();
+ }
+
+ ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+ return mGutsManager::openGuts;
+ }
+
+ private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
+ mUiOffloadThread.submit(() -> {
+ try {
+ mBarService.onNotificationExpansionChanged(key, userAction, expanded);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ });
+ }
+
+ /** Callback for when a row is bound to an entry. */
+ public interface BindRowCallback {
+ /**
+ * Called when a new notification and row is created.
+ *
+ * @param entry entry for the notification
+ * @param pmUser package manager for user
+ * @param sbn notification
+ * @param row row for the notification
+ */
+ void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fce7980..abb7b416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -25,10 +25,14 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* A manager that ensures that notifications are visually stable. It will suppress reorderings
* and reorder at the right time when they are out of view.
*/
+@Singleton
public class VisualStabilityManager implements OnHeadsUpChangedListener {
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
@@ -42,6 +46,10 @@
private ArraySet<View> mAddedChildren = new ArraySet<>();
private boolean mPulsing;
+ @Inject
+ public VisualStabilityManager() {
+ }
+
/**
* Add a callback to invoke when reordering is allowed again.
* @param callback
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9f02e54..eb1fc30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -40,10 +40,14 @@
import java.util.Collection;
import java.util.Collections;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles notification logging, in particular, logging which notifications are visible and which
* are not.
*/
+@Singleton
public class NotificationLogger implements StateListener {
private static final String TAG = "NotificationLogger";
@@ -145,6 +149,7 @@
}
};
+ @Inject
public NotificationLogger() {
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1d79152..8b0a682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -800,7 +800,7 @@
}
@Override
- public void performRemoveAnimation(long duration, long delay,
+ public long performRemoveAnimation(long duration, long delay,
float translationDirection, boolean isHeadsUpAnimation, float endLocation,
Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
@@ -812,6 +812,7 @@
} else if (onFinishedRunnable != null) {
onFinishedRunnable.run();
}
+ return 0;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 91d08ff..a58c7cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2612,6 +2612,29 @@
}
@Override
+ public long performRemoveAnimation(long duration, long delay, float translationDirection,
+ boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener) {
+ if (mMenuRow.isMenuVisible()) {
+ Animator anim = getTranslateViewAnimator(0f, null /* listener */);
+ if (anim != null) {
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ExpandableNotificationRow.super.performRemoveAnimation(
+ duration, delay, translationDirection, isHeadsUpAnimation,
+ endLocation, onFinishedRunnable, animationListener);
+ }
+ });
+ anim.start();
+ return anim.getDuration();
+ }
+ }
+ return super.performRemoveAnimation(duration, delay, translationDirection,
+ isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+ }
+
+ @Override
protected void onAppearAnimationFinished(boolean wasAppearing) {
super.onAppearAnimationFinished(wasAppearing);
if (wasAppearing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index b1fa6a5..d1a89b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -312,16 +312,19 @@
* @param duration The duration of the remove animation.
* @param delay The delay of the animation
* @param translationDirection The direction value from [-1 ... 1] indicating in which the
- * animation should be performed. A value of -1 means that The
- * remove animation should be performed upwards,
- * such that the child appears to be going away to the top. 1
- * Should mean the opposite.
+ * animation should be performed. A value of -1 means that The
+ * remove animation should be performed upwards,
+ * such that the child appears to be going away to the top. 1
+ * Should mean the opposite.
* @param isHeadsUpAnimation Is this a headsUp animation.
* @param endLocation The location where the horizonal heads up disappear animation should end.
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
* @param animationListener An animation listener to add to the animation.
+ *
+ * @return The additional delay, in milliseconds, that this view needs to add before the
+ * animation starts.
*/
- public abstract void performRemoveAnimation(long duration,
+ public abstract long performRemoveAnimation(long duration,
long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 16796dd..607d96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -34,10 +34,14 @@
import java.util.HashSet;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manager for the notification blocking helper - tracks and helps create the blocking helper
* affordance.
*/
+@Singleton
public class NotificationBlockingHelperManager {
/** Enables debug logging and always makes the blocking helper show up after a dismiss. */
private static final boolean DEBUG = false;
@@ -54,6 +58,7 @@
*/
private boolean mIsShadeExpanded;
+ @Inject
public NotificationBlockingHelperManager(Context context) {
mContext = context;
mNonBlockablePkgs = new HashSet<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 2e45527..ac4e583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -57,10 +57,14 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
* closing guts, and keeping track of the currently exposed notification guts.
*/
+@Singleton
public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
private static final String TAG = "NotificationGutsManager";
@@ -91,6 +95,7 @@
@VisibleForTesting
protected String mKeyToRemoveOnGutsClosed;
+ @Inject
public NotificationGutsManager(Context context) {
mContext = context;
mAccessibilityManager = (AccessibilityManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 1b40c06..eaa2eaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -190,12 +190,13 @@
}
@Override
- public void performRemoveAnimation(long duration, long delay,
+ public long performRemoveAnimation(long duration, long delay,
float translationDirection, boolean isHeadsUpAnimation, float endLocation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
setContentVisible(false);
+ return 0;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dbe6e8e..f982ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,10 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
- .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
- .ANIMATION_DURATION_SWIPE;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
import android.animation.Animator;
@@ -651,6 +649,8 @@
< mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
|| mAmbientState.isDark())) {
drawBackground(canvas);
+ } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
+ drawHeadsUpBackground(canvas);
}
if (DEBUG) {
@@ -749,6 +749,32 @@
mCornerRadius, mCornerRadius, mBackgroundPaint);
}
+ private void drawHeadsUpBackground(Canvas canvas) {
+ int left = mSidePaddings;
+ int right = getWidth() - mSidePaddings;
+
+ float top = getHeight();
+ float bottom = 0;
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE
+ && child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
+ top = Math.min(top, row.getTranslationY());
+ bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
+ }
+ }
+ }
+
+ if (top < bottom) {
+ canvas.drawRoundRect(
+ left, top, right, bottom,
+ mCornerRadius, mCornerRadius, mBackgroundPaint);
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateBackgroundDimming() {
// No need to update the background color if it's not being drawn.
@@ -2157,19 +2183,7 @@
}
return;
}
- NotificationSection firstSection = getFirstVisibleSection();
- int top = 0;
- if (firstSection != null) {
- ActivatableNotificationView firstView = firstSection.getFirstVisibleChild();
- // Round Y up to avoid seeing the background during animation
- int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
- if (mAnimateNextBackgroundTop || firstSection.isTargetTop(finalTranslationY)) {
- // we're ending up at the same location as we are now, lets just skip the animation
- top = finalTranslationY;
- } else {
- top = (int) Math.ceil(firstView.getTranslationY());
- }
- }
+ int top = getSectionTopOrFinalTop(getFirstVisibleSection(), mAnimateNextBackgroundTop);
NotificationSection lastSection = getLastVisibleSection();
ActivatableNotificationView lastView =
mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
@@ -2177,21 +2191,8 @@
: lastSection == null ? null : lastSection.getLastVisibleChild();
int bottom;
if (lastView != null) {
- int finalTranslationY;
- if (lastView == mShelf) {
- finalTranslationY = (int) mShelf.getTranslationY();
- } else {
- finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
- }
- int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
- int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
- if (mAnimateNextBackgroundBottom || lastSection.isTargetBottom(finalBottom)) {
- // we're ending up at the same location as we are now, lets just skip the animation
- bottom = finalBottom;
- } else {
- bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
- - lastView.getClipBottomAmount());
- }
+ bottom = getSectionBottomOrFinalBottom(
+ lastSection, lastView, mAnimateNextBackgroundBottom);
} else {
top = mTopPadding;
bottom = top;
@@ -2207,6 +2208,57 @@
setSectionBoundsByPriority(left, right, top, bottom, mSections[0], mSections[1]);
}
+ private int getSectionTopOrFinalTop(
+ @Nullable NotificationSection section, boolean alreadyAnimating) {
+ int top = 0;
+ if (section != null) {
+ ActivatableNotificationView firstView = section.getFirstVisibleChild();
+ // Round Y up to avoid seeing the background during animation
+ int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+ if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
+ // we're ending up at the same location as we are now, let's just skip the animation
+ top = finalTranslationY;
+ } else {
+ top = (int) Math.ceil(firstView.getTranslationY());
+ }
+ }
+ return top;
+ }
+
+ private int getSectionBottomOrFinalBottom(
+ @Nullable NotificationSection section, boolean alreadyAnimating) {
+ return section == null ? 0
+ : getSectionBottomOrFinalBottom(
+ section, section.getLastVisibleChild(), alreadyAnimating);
+ }
+
+ private int getSectionBottomOrFinalBottom(
+ NotificationSection section,
+ ActivatableNotificationView lastView,
+ boolean alreadyAnimating) {
+ int bottom = 0;
+ if (lastView != null) {
+ float finalTranslationY;
+ if (lastView == mShelf) {
+ finalTranslationY = mShelf.getTranslationY();
+ } else {
+ finalTranslationY = ViewState.getFinalTranslationY(lastView);
+ }
+ int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
+ // Round Y down to avoid seeing the background during animation
+ int finalBottom = (int) Math.floor(
+ finalTranslationY + finalHeight - lastView.getClipBottomAmount());
+ if (alreadyAnimating || section.isTargetBottom(finalBottom)) {
+ // we're ending up at the same location as we are now, lets just skip the animation
+ bottom = finalBottom;
+ } else {
+ bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+ - lastView.getClipBottomAmount());
+ }
+ }
+ return bottom;
+ }
+
private void setSectionBoundsByPriority(int left, int right, int top, int bottom,
NotificationSection highPrioritySection, NotificationSection lowPrioritySection) {
if (NotificationUtils.useNewInterruptionModel(mContext)) {
@@ -2214,13 +2266,14 @@
ActivatableNotificationView lastChildAboveGap = getLastHighPriorityChild();
ActivatableNotificationView firstChildBelowGap = getFirstLowPriorityChild();
if (lastChildAboveGap != null && firstChildBelowGap != null) {
- int gapTop =
- (int) Math.max(top,
- Math.min(lastChildAboveGap.getTranslationY()
- + lastChildAboveGap.getActualHeight(),
- bottom));
- int gapBottom = (int) Math.max(top,
- Math.min(firstChildBelowGap.getTranslationY(), bottom));
+ int gapTop = getSectionBottomOrFinalBottom(
+ highPrioritySection, mAnimateNextSectionBoundsChange);
+ gapTop = Math.max(top, Math.min(gapTop, bottom));
+
+ int gapBottom = getSectionTopOrFinalTop(
+ lowPrioritySection, mAnimateNextSectionBoundsChange);
+ gapBottom = Math.max(top, Math.min(gapBottom, bottom));
+
highPrioritySection.getBounds().set(left, top, right, gapTop);
lowPrioritySection.getBounds().set(left, gapBottom, right, bottom);
} else if (lastChildAboveGap != null) {
@@ -5574,15 +5627,21 @@
if (translatingParentView != null && row == translatingParentView) {
mSwipeHelper.clearExposedMenuView();
mSwipeHelper.clearTranslatingParentView();
+ if (row instanceof ExpandableNotificationRow) {
+ mHeadsUpManager.setMenuShown(
+ ((ExpandableNotificationRow) row).getEntry(), false);
+
+ }
}
}
@Override
public void onMenuShown(View row) {
if (row instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
- ((ExpandableNotificationRow) row).getStatusBarNotification()
- .getPackageName());
+ notificationRow.getStatusBarNotification().getPackageName());
+ mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
}
mSwipeHelper.onMenuShown(row);
}
@@ -5764,7 +5823,7 @@
(int) (dragLengthY / mDisplayMetrics.density),
0 /* velocityDp - N/A */);
- if (mNotificationPanel.onDraggedDown() || startingChild != null) {
+ if (!mAmbientState.isDark() || startingChild != null) {
// We have notifications, go to locked shade.
mShadeController.goToLockedShade(startingChild);
if (startingChild instanceof ExpandableNotificationRow) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d690547..19fce48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -406,13 +406,8 @@
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
- 0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, new Runnable() {
- @Override
- public void run() {
- removeTransientView(changingView);
- }
- }, null);
+ 0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
+ 0, () -> removeTransientView(changingView), null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
@@ -507,10 +502,11 @@
// We need to add the global animation listener, since once no animations are
// running anymore, the panel will instantly hide itself. We need to wait until
// the animation is fully finished for this though.
- changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
- + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
- true /* isHeadsUpAppear */, targetLocation, endRunnable,
- getGlobalAnimationFinishedListener());
+ long removeAnimationDelay = changingView.performRemoveAnimation(
+ ANIMATION_DURATION_HEADS_UP_DISAPPEAR + ANIMATION_DELAY_HEADS_UP,
+ extraDelay, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+ endRunnable, getGlobalAnimationFinishedListener());
+ mAnimationProperties.delay += removeAnimationDelay;
} else if (endRunnable != null) {
endRunnable.run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 5906dc2..5b44a77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -29,6 +29,12 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class DarkIconDispatcherImpl implements DarkIconDispatcher {
private final LightBarTransitionsController mTransitionsController;
@@ -40,6 +46,9 @@
private int mDarkModeIconColorSingleTone;
private int mLightModeIconColorSingleTone;
+ /**
+ */
+ @Inject
public DarkIconDispatcherImpl(Context context) {
mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index aa0b7b6..f4cfd41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -264,6 +264,17 @@
}
}
+ /**
+ * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
+ * area if it's pinned until it's hidden again.
+ */
+ public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
+ if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
+ ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// HeadsUpManager public methods overrides:
@@ -469,6 +480,14 @@
// HeadsUpEntryPhone:
protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+
+ private boolean mMenuShownPinned;
+
+ @Override
+ protected boolean isSticky() {
+ return super.isSticky() || mMenuShownPinned;
+ }
+
public void setEntry(@NonNull final NotificationData.Entry entry) {
Runnable removeHeadsUpRunnable = () -> {
if (!mVisualStabilityManager.isReorderingAllowed()) {
@@ -510,6 +529,25 @@
updateEntry(false /* updatePostTime */);
}
}
+
+ public void setMenuShownPinned(boolean menuShownPinned) {
+ if (mMenuShownPinned == menuShownPinned) {
+ return;
+ }
+
+ mMenuShownPinned = menuShownPinned;
+ if (menuShownPinned) {
+ removeAutoRemovalCallbacks();
+ } else {
+ updateEntry(false /* updatePostTime */);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ mMenuShownPinned = false;
+ }
}
public interface AnimationStateHandler {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index b3d0bf8..e541e14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -20,15 +20,23 @@
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Executes actions that require the screen to be unlocked. Delegates the actual handling to an
* implementation passed via {@link #setDismissHandler}.
*/
+@Singleton
public class KeyguardDismissUtil implements KeyguardDismissHandler {
private static final String TAG = "KeyguardDismissUtil";
private volatile KeyguardDismissHandler mDismissHandler;
+ @Inject
+ public KeyguardDismissUtil() {
+ }
+
/** Sets the actual {@link DismissHandler} implementation. */
public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
mDismissHandler = dismissHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 96b7536..5ba59b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -466,6 +466,14 @@
.onDensityOrFontScaleChanged();
}
+ @Override
+ public void onOverlayChanged() {
+ mCarrierLabel.setTextAppearance(
+ Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall));
+ onThemeChanged();
+ mBatteryView.updatePercentView();
+ }
+
private void updateIconsAndTextColors() {
@ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
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 7c30e48..e156e77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -34,9 +34,13 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Controls how light status bar flag applies to the icons.
*/
+@Singleton
public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable {
private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
@@ -78,6 +82,7 @@
private final Context mContext;
+ @Inject
public LightBarController(Context ctx) {
mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
mStatusBarIconController = Dependency.get(DarkIconDispatcher.class);
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 7876aa5..dd07ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME;
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;
@@ -161,7 +162,7 @@
public long getTintAnimationDuration() {
if (NavBarTintController.isEnabled(mContext)) {
return Math.max(Settings.Global.getInt(mContext.getContentResolver(),
- NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION),
+ NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_COLOR_ADAPT_TRANSITION_TIME),
MIN_COLOR_ADAPT_TRANSITION_TIME);
}
return DEFAULT_TINT_ANIMATION_DURATION;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 7621887..1281953 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -25,16 +25,21 @@
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Wrapper that emits both new- and old-style gesture logs.
* TODO: delete this once the old logs are no longer needed.
*/
+@Singleton
public class LockscreenGestureLogger {
private ArrayMap<Integer, Integer> mLegacyMap;
private LogMaker mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
.setType(MetricsEvent.TYPE_ACTION);
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ @Inject
public LockscreenGestureLogger() {
mLegacyMap = new ArrayMap<>(EventLogConstants.METRICS_GESTURE_TYPE_MAP.length);
for (int i = 0; i < EventLogConstants.METRICS_GESTURE_TYPE_MAP.length ; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 0f8d59b..fbd8d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -28,6 +28,12 @@
import java.util.LinkedList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class ManagedProfileControllerImpl implements ManagedProfileController {
private final List<Callback> mCallbacks = new ArrayList<>();
@@ -38,6 +44,9 @@
private boolean mListening;
private int mCurrentUser;
+ /**
+ */
+ @Inject
public ManagedProfileControllerImpl(Context context) {
mContext = context;
mUserManager = UserManager.get(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
index 9ecee18..b4f850b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -31,6 +31,7 @@
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;
+ public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1500;
private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread");
private Handler mColorAdaptionHandler;
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 55655d5..2daff2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -72,7 +72,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
@@ -97,6 +96,8 @@
import java.util.Locale;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Fragment containing the NavigationBarFragment. Contains logic for what happens
* on clicks and view states of the nav bar.
@@ -111,11 +112,12 @@
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
- private final DeviceProvisionedController mDeviceProvisionedController =
- Dependency.get(DeviceProvisionedController.class);
+ private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ protected final AssistManager mAssistManager;
+ private final MetricsLogger mMetricsLogger;
+ private final DeviceProvisionedController mDeviceProvisionedController;
protected NavigationBarView mNavigationBarView = null;
- protected AssistManager mAssistManager;
private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -124,7 +126,6 @@
private AccessibilityManager mAccessibilityManager;
private MagnificationContentObserver mMagnificationObserver;
private ContentResolver mContentResolver;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -193,6 +194,17 @@
}
};
+ @Inject
+ public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
+ DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
+ AssistManager assistManager, OverviewProxyService overviewProxyService) {
+ mAccessibilityManagerWrapper = accessibilityManagerWrapper;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mMetricsLogger = metricsLogger;
+ mAssistManager = assistManager;
+ mOverviewProxyService = overviewProxyService;
+ }
+
// ----- Fragment Lifecycle Callbacks -----
@Override
@@ -205,8 +217,6 @@
mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
mWindowManager = getContext().getSystemService(WindowManager.class);
mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
- Dependency.get(AccessibilityManagerWrapper.class).addCallback(
- mAccessibilityListener);
mContentResolver = getContext().getContentResolver();
mMagnificationObserver = new MagnificationContentObserver(
getContext().getMainThreadHandler());
@@ -218,15 +228,13 @@
mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
}
- mAssistManager = Dependency.get(AssistManager.class);
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
}
@Override
public void onDestroy() {
super.onDestroy();
- Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
- mAccessibilityListener);
+ mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
mContentResolver.unregisterContentObserver(mMagnificationObserver);
}
@@ -892,7 +900,8 @@
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
- final NavigationBarFragment fragment = new NavigationBarFragment();
+ final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
+ .create(NavigationBarFragment.class);
navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
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 6a7983a..8c17922 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -276,7 +276,9 @@
@Override
public void onBackButtonVisibilityChanged(boolean visible) {
- getBackButton().setVisibility(visible ? VISIBLE : GONE);
+ if (!inScreenPinning()) {
+ getBackButton().setVisibility(visible ? VISIBLE : GONE);
+ }
}
@Override
@@ -940,6 +942,9 @@
public void showPinningEnterExitToast(boolean entering) {
if (entering) {
mScreenPinningNotify.showPinningStartToast();
+
+ // TODO(b/112934365): remove after prototype finished, only needed to escape from pin
+ getBackButton().setVisibility(VISIBLE);
} else {
mScreenPinningNotify.showPinningExitToast();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index dd81c4e..b1f74c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -29,8 +29,8 @@
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
@@ -42,11 +42,15 @@
import java.util.ArrayList;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* A helper class dealing with the alert interactions between {@link NotificationGroupManager},
* {@link HeadsUpManager}, {@link AmbientPulseManager}. In particular, this class deals with keeping
* the correct notification in a group alerting based off the group suppression.
*/
+@Singleton
public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
OnAmbientChangedListener, StateListener {
@@ -73,6 +77,7 @@
private boolean mIsDozing;
+ @Inject
public NotificationGroupAlertTransferHelper() {
Dependency.get(StatusBarStateController.class).addCallback(this);
}
@@ -90,7 +95,7 @@
// not being up to date.
mEntryManager = entryManager;
- mEntryManager.setAlertTransferListener(mAlertTransferListener);
+ mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
groupManager.addOnGroupChangeListener(mOnGroupChangeListener);
}
@@ -181,7 +186,8 @@
}
}
- private final AlertTransferListener mAlertTransferListener = new AlertTransferListener() {
+ private final NotificationEntryListener mNotificationEntryListener =
+ new NotificationEntryListener() {
// Called when a new notification has been posted but is not inflated yet. We use this to
// see as early as we can if we need to abort a transfer.
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8f4369a..3c1c076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -39,9 +39,13 @@
import java.util.Map;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* A class to handle notifications and their corresponding groups.
*/
+@Singleton
public class NotificationGroupManager implements OnHeadsUpChangedListener,
OnAmbientChangedListener, StateListener {
@@ -54,6 +58,7 @@
private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
private boolean mIsUpdatingUnchangedGroup;
+ @Inject
public NotificationGroupManager() {
Dependency.get(StatusBarStateController.class).addCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 2d5d562..e40835f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -207,7 +207,7 @@
}
// showAmbient == show in shade but not shelf
- if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
+ if (!showAmbient && entry.shouldSuppressStatusBar()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c7e4d34..242573d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -38,7 +38,6 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.PowerManager;
-import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
@@ -53,6 +52,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
@@ -106,23 +106,6 @@
private static final boolean DEBUG = false;
/**
- * If passive interrupts expand the NSSL or not
- */
- private static final boolean EXPAND_ON_PASSIVE_INTERRUPT = SystemProperties.getBoolean(
- "persist.sysui.expand_shade_on_wake_up", true);
- /**
- * If the notification panel should remain collapsed when the phone wakes up, even if the user
- * presses power.
- */
- private static final boolean NEVER_EXPAND_WHEN_WAKING_UP = SystemProperties.getBoolean(
- "persist.sysui.defer_notifications_on_lock_screen", false);
- /**
- * If waking up the phone should take you to SHADE_LOCKED instead of KEYGUARD
- */
- private static final boolean WAKE_UP_TO_SHADE = SystemProperties.getBoolean(
- "persist.sysui.go_to_shade_on_wake_up", false);
-
- /**
* Fling expanding QS.
*/
public static final int FLING_EXPAND = 0;
@@ -133,7 +116,7 @@
public static final int FLING_COLLAPSE = 1;
/**
- * Fing until QS is completely hidden.
+ * Fling until QS is completely hidden.
*/
public static final int FLING_HIDE = 2;
@@ -282,12 +265,6 @@
*/
private float mLinearDarkAmount;
- /**
- * State where the device isn't dozing anymore, but the lock screen isn't fully awake.
- * The screen will be dimmed down with the shade collapsed.
- */
- private boolean mSemiAwake;
-
private boolean mPulsing;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mNoVisibleNotifications = true;
@@ -359,6 +336,10 @@
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
+ KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
+ ViewGroup bigClockContainer = findViewById(R.id.big_clock_container);
+ keyguardClockSwitch.setBigClockContainer(bigClockContainer);
+
mNotificationContainerParent = findViewById(R.id.notification_container_parent);
mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -1251,11 +1232,6 @@
updateDozingVisibilities(false /* animate */);
}
- // Expand notification shade if the device was is semi-awake state
- if (mBarState == StatusBarState.SHADE && isSemiAwake()) {
- mNotificationStackScroller.setDark(false /* dark */, false /* animated */,
- null /* touchLocation */);
- }
resetVerticalPanelPosition();
updateQsState();
}
@@ -2785,35 +2761,30 @@
mNotificationStackScroller.setAnimationsEnabled(!disabled);
}
- public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation,
- boolean passivelyInterrupted) {
+ /**
+ * Sets the dozing state.
+ *
+ * @param dozing {@code true} when dozing.
+ * @param animate if transition should be animated.
+ * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
+ */
+ public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
if (dozing == mDozing) return;
mDozing = dozing;
- boolean doNotExpand = (!EXPAND_ON_PASSIVE_INTERRUPT && passivelyInterrupted)
- || NEVER_EXPAND_WHEN_WAKING_UP;
- mSemiAwake = doNotExpand && !mDozing;
- if (!mSemiAwake) {
- mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
- }
+ mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
if (mBarState == StatusBarState.KEYGUARD
|| mBarState == StatusBarState.SHADE_LOCKED) {
updateDozingVisibilities(animate);
}
- final float darkAmount = dozing && !mSemiAwake ? 1 : 0;
- if (!mSemiAwake) {
- mStatusBarStateController.setDozeAmount(darkAmount, animate);
- }
+ final float darkAmount = dozing ? 1 : 0;
+ mStatusBarStateController.setDozeAmount(darkAmount, animate);
if (animate) {
mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
}
}
- public boolean isSemiAwake() {
- return mSemiAwake;
- }
-
@Override
public void onDozeAmountChanged(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
@@ -3006,23 +2977,4 @@
public void showTransientIndication(int id) {
mKeyguardBottomArea.showTransientIndication(id);
}
-
- /**
- * Whenever a user drags down on the empty area (pulling down the shade and clock) and lets go.
- *
- * @return {@code true} if dragging down should take the user to SHADE_LOCKED.
- */
- public boolean onDraggedDown() {
- if (isSemiAwake()) {
- mSemiAwake = false;
- mNotificationStackScroller.setDark(false /* dark */, true /* animate */,
- null /* touchLocation */);
- mStatusBarStateController.setDozeAmount(0f, true /* animated */);
- mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
- mStatusBar.updateScrimController();
-
- return WAKE_UP_TO_SHADE;
- }
- return true;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index ee1eb42..d6f2fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -85,7 +85,6 @@
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
@@ -102,9 +101,8 @@
* strictly doesn't need to.
*/
public class PhoneStatusBarPolicy implements Callback, Callbacks,
- RotationLockControllerCallback, Listener, LocationChangeCallback,
- ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback,
- PrivacyItemController.Callback {
+ RotationLockControllerCallback, Listener, ZenModeController.Callback,
+ DeviceProvisionedListener, KeyguardMonitor.Callback, PrivacyItemController.Callback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -255,6 +253,9 @@
mIconController.setIconVisibility(mSlotMicrophone, false);
mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null);
mIconController.setIconVisibility(mSlotCamera, false);
+ mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
+ mContext.getString(R.string.accessibility_location_active));
+ mIconController.setIconVisibility(mSlotLocation, false);
mRotationLockController.addCallback(this);
mBluetooth.addCallback(this);
@@ -265,7 +266,6 @@
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
- mLocationController.addCallback(this);
mPrivacyItemController.setListening(true);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
@@ -294,7 +294,6 @@
mNextAlarmController.removeCallback(mNextAlarmCallback);
mDataSaver.removeCallback(this);
mKeyguardMonitor.removeCallback(this);
- mLocationController.removeCallback(this);
mPrivacyItemController.setListening(false);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
mContext.unregisterReceiver(mIntentReceiver);
@@ -314,21 +313,6 @@
updateVolumeZen();
}
- @Override
- public void onLocationActiveChanged(boolean active) {
- updateLocation();
- }
-
- // Updates the status view based on the current state of location requests.
- private void updateLocation() {
- if (mLocationController.isLocationActive()) {
- mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
- mContext.getString(R.string.accessibility_location_active));
- } else {
- mIconController.removeAllIconsForSlot(mSlotLocation);
- }
- }
-
private void updateAlarm() {
final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 9f34cbb..6a4da98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -282,8 +282,7 @@
mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
}
- // Do not re-schedule timeout when pulsing, let's save some extra battery.
- } else if (mState != ScrimState.PULSING) {
+ } else {
mTimeTicker.cancel();
mWallpaperVisibilityTimedOut = false;
}
@@ -365,7 +364,7 @@
mExpansionFraction = fraction;
final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
- || mState == ScrimState.KEYGUARD || mState == ScrimState.DARK_KEYGUARD;
+ || mState == ScrimState.KEYGUARD;
if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 75f81c3..226665e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import android.graphics.Color;
-import android.os.SystemProperties;
import android.os.Trace;
import android.util.MathUtils;
@@ -68,27 +67,6 @@
},
/**
- * On semi-awake lock screen.
- */
- DARK_KEYGUARD(7) {
-
- @Override
- public void prepare(ScrimState previousState) {
- mBlankScreen = mDisplayRequiresBlanking && previousState != ScrimState.AOD;
- mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
- String opacity = SystemProperties.get("persist.sysui.aod2_scrim_opacity", "0.8");
- try {
- mCurrentBehindAlpha = Float.parseFloat(opacity);
- } catch (RuntimeException e) {
- mCurrentBehindAlpha = ScrimController.GRADIENT_SCRIM_DARK_KEYGUARD;
- }
- mCurrentInFrontAlpha = 0;
- mCurrentInFrontTint = Color.BLACK;
- mCurrentBehindTint = Color.BLACK;
- }
- },
-
- /**
* Showing password challenge on the keyguard.
*/
BOUNCER(1) {
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 1e70912..465187d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -151,6 +151,7 @@
import com.android.systemui.doze.LockScreenWakeUpController;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -191,6 +192,8 @@
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -376,6 +379,8 @@
private NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
+ private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private NotificationRowBinder mNotificationRowBinder;
protected NotificationViewHierarchyManager mViewHierarchyManager;
protected ForegroundServiceController mForegroundServiceController;
protected AppOpsController mAppOpsController;
@@ -620,6 +625,9 @@
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+ mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
mAppOpsController = Dependency.get(AppOpsController.class);
@@ -630,6 +638,12 @@
mNavigationBarController = Dependency.get(DisplayNavigationBarController.class);
mBubbleController = Dependency.get(BubbleController.class);
mBubbleController.setExpandListener(mBubbleExpandListener);
+ KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
+ if (sliceProvider != null) {
+ sliceProvider.initDependencies();
+ } else {
+ Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
+ }
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
@@ -1014,7 +1028,7 @@
}
protected QS createDefaultQSFragment() {
- return new QSFragment();
+ return FragmentHostManager.get(mStatusBarWindow).create(QSFragment.class);
}
private void setUpPresenter() {
@@ -1035,10 +1049,10 @@
mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
+ mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
+ this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
- mEntryManager.setNotificationClicker(new NotificationClicker(
- this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
}
/**
@@ -1164,6 +1178,10 @@
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onOverlayChanged();
}
+ // We need the new R.id.keyguard_indication_area before recreating
+ // mKeyguardIndicationController
+ mNotificationPanel.onThemeChanged();
+ onThemeChanged();
}
@Override
@@ -1406,7 +1424,7 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- mEntryManager.setDisableNotificationAlerts(
+ mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
(state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
}
@@ -3301,12 +3319,7 @@
boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup())
|| (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
- mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation,
- mDozeServiceHost.wasPassivelyInterrupted());
- if (mNotificationPanel.isSemiAwake()
- && SystemProperties.getBoolean("persist.systemui.show_swipe_up", false)) {
- mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
- }
+ mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
updateQsExpansionEnabled();
Trace.endSection();
}
@@ -3872,9 +3885,6 @@
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
- if (mNotificationPanel.isSemiAwake()) {
- state = ScrimState.DARK_KEYGUARD;
- }
mScrimController.transitionTo(state);
} else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
|| launchingAffordanceWithPreview) {
@@ -3887,8 +3897,7 @@
} else if (mDozing && !wakeAndUnlocking) {
mScrimController.transitionTo(ScrimState.AOD);
} else if (mIsKeyguard && !wakeAndUnlocking) {
- mScrimController.transitionTo(mNotificationPanel.isSemiAwake()
- ? ScrimState.DARK_KEYGUARD : ScrimState.KEYGUARD);
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
} else if (mBubbleController.isStackExpanded()) {
mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
} else {
@@ -3911,7 +3920,6 @@
private boolean mAnimateWakeup;
private boolean mAnimateScreenOff;
private boolean mIgnoreTouchWhilePulsing;
- private boolean mPassivelyInterrupted;
@Override
public String toString() {
@@ -4003,11 +4011,6 @@
}
@Override
- public void setPassiveInterrupt(boolean passiveInterrupt) {
- mPassivelyInterrupted = passiveInterrupt;
- }
-
- @Override
public void onIgnoreTouchWhilePulsing(boolean ignore) {
if (ignore != mIgnoreTouchWhilePulsing) {
DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
@@ -4054,11 +4057,6 @@
}
@Override
- public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
- StatusBar.this.startPendingIntentDismissingKeyguard(intent);
- }
-
- @Override
public void extendPulse() {
if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) {
mAmbientPulseManager.extendPulse();
@@ -4120,10 +4118,6 @@
public boolean shouldAnimateScreenOff() {
return mAnimateScreenOff;
}
-
- public boolean wasPassivelyInterrupted() {
- return mPassivelyInterrupted;
- }
}
public boolean shouldIgnoreTouch() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 4f25349..db7589d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -45,11 +45,15 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Receives the callbacks from CommandQueue related to icons and tracks the state of
* all the icons. Dispatches this state to any IconManagers that are currently
* registered with it.
*/
+@Singleton
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
@@ -66,6 +70,7 @@
private boolean mIsDark = false;
+ @Inject
public StatusBarIconControllerImpl(Context context) {
super(context.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index c93d151..8d1b911 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -24,6 +24,7 @@
import android.app.ActivityTaskManager;
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
@@ -33,15 +34,21 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.EventLog;
import android.util.Log;
import android.view.RemoteAnimationAdapter;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
@@ -54,7 +61,9 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -66,6 +75,7 @@
public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
private static final String TAG = "NotificationClickHandler";
+ protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
private final NotificationGroupManager mGroupManager =
@@ -84,6 +94,9 @@
Dependency.get(NotificationEntryManager.class);
private final StatusBarStateController mStatusBarStateController =
Dependency.get(StatusBarStateController.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+ private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Context mContext;
private final NotificationPanelView mNotificationPanel;
@@ -94,6 +107,7 @@
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private final IStatusBarService mBarService;
private final CommandQueue mCommandQueue;
+ private final IDreamManager mDreamManager;
private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -112,6 +126,15 @@
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mCommandQueue = getComponent(context, CommandQueue.class);
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ @Override
+ public void onPendingEntryAdded(NotificationData.Entry entry) {
+ handleFullScreenIntent(entry);
+ }
+ });
}
/**
@@ -322,6 +345,45 @@
}, null, false /* afterKeyguardGone */);
}
+ private void handleFullScreenIntent(NotificationData.Entry entry) {
+ boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+ if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
+ if (shouldSuppressFullScreenIntent(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
+ }
+ } else if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
+ }
+ } else {
+ // Stop screensaver if the notification has a fullscreen intent.
+ // (like an incoming phone call)
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ try {
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ });
+
+ // not immersive & a fullscreen alert should be shown
+ if (DEBUG) {
+ Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+ }
+ try {
+ EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+ entry.key);
+ entry.notification.getNotification().fullScreenIntent.send();
+ entry.notifyFullScreenIntentLaunched();
+ mMetricsLogger.count("note_fullscreen", 1);
+ } catch (PendingIntent.CanceledException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
@Override
public boolean isCollapsingToShowActivityOverLockscreen() {
return mIsCollapsingToShowActivityOverLockscreen;
@@ -351,6 +413,14 @@
|| !mActivityLaunchAnimator.isAnimationPending();
}
+ private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
+ if (mPresenter.isDeviceInVrMode()) {
+ return true;
+ }
+
+ return entry.shouldSuppressFullScreenIntent();
+ }
+
private void removeNotification(StatusBarNotification notification) {
// We have to post it to the UI thread for synchronization
Dependency.get(MAIN_HANDLER).post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 261f117..d643f07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -57,7 +57,10 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -88,6 +91,10 @@
Dependency.get(StatusBarStateController.class);
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
+ private final NotificationRowBinder mNotificationRowBinder =
+ Dependency.get(NotificationRowBinder.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
@@ -166,8 +173,37 @@
NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
Dependency.get(InitController.class).addPostInitTask(() -> {
+ NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
+ @Override
+ public void onNotificationAdded(Entry entry) {
+ // Recalculate the position of the sliding windows and the titles.
+ mShadeController.updateAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationUpdated(StatusBarNotification notification) {
+ mShadeController.updateAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationRemoved(String key, StatusBarNotification old) {
+ StatusBarNotificationPresenter.this.onNotificationRemoved(key, old);
+ }
+
+ @Override
+ public void onPerformRemoveNotification(
+ StatusBarNotification statusBarNotification) {
+ StatusBarNotificationPresenter.this.onPerformRemoveNotification();
+ }
+ };
+
mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
- mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(
+ this, notifListContainer, notificationEntryListener, mHeadsUpManager);
+ mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
+ mEntryManager, this);
+ mNotificationInterruptionStateProvider.setUpWithPresenter(
+ this, mHeadsUpManager, this::canHeadsUp);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this,
@@ -217,10 +253,9 @@
|| mActivityLaunchAnimator.isAnimationRunning();
}
- @Override
- public void onPerformRemoveNotification(StatusBarNotification n) {
+ private void onPerformRemoveNotification() {
if (mNotificationPanel.hasPulsingNotifications() &&
- !mAmbientPulseManager.hasNotifications()) {
+ !mAmbientPulseManager.hasNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -244,18 +279,6 @@
mNotificationPanel.updateNotificationViews();
}
- @Override
- public void onNotificationAdded(Entry shadeEntry) {
- // Recalculate the position of the sliding windows and the titles.
- mShadeController.updateAreThereNotifications();
- }
-
- @Override
- public void onNotificationUpdated(StatusBarNotification notification) {
- mShadeController.updateAreThereNotifications();
- }
-
- @Override
public void onNotificationRemoved(String key, StatusBarNotification old) {
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -277,7 +300,6 @@
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
- @Override
public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
if (mShadeController.isDozing()) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 78f5374..116ecc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -47,6 +47,12 @@
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.PreviewInflater;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class StatusBarRemoteInputCallback implements Callback, Callbacks {
private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
@@ -65,6 +71,9 @@
private int mDisabled2;
protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
+ /**
+ */
+ @Inject
public StatusBarRemoteInputCallback(Context context) {
mContext = context;
mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index cb6e300..88f9048 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -54,9 +54,13 @@
import java.io.PrintWriter;
import java.lang.reflect.Field;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Encapsulates all logic for the status bar window state management.
*/
+@Singleton
public class StatusBarWindowController implements Callback, Dumpable, ConfigurationListener {
private static final String TAG = "StatusBarWindowController";
@@ -78,6 +82,7 @@
private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ @Inject
public StatusBarWindowController(Context context) {
this(context, context.getSystemService(WindowManager.class), ActivityManager.getService(),
DozeParameters.getInstance(context));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
index cc431dd..ebfdb3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
@@ -23,6 +23,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class AccessibilityController implements
AccessibilityManager.AccessibilityStateChangeListener,
AccessibilityManager.TouchExplorationStateChangeListener {
@@ -32,6 +38,9 @@
private boolean mAccessibilityEnabled;
private boolean mTouchExplorationEnabled;
+ /**
+ */
+ @Inject
public AccessibilityController(Context context) {
AccessibilityManager am =
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
index 67da8a5..1395e13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
@@ -19,14 +19,19 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* For mocking because AccessibilityManager is final for some reason...
*/
+@Singleton
public class AccessibilityManagerWrapper implements
CallbackController<AccessibilityServicesStateChangeListener> {
private final AccessibilityManager mAccessibilityManager;
+ @Inject
public AccessibilityManagerWrapper(Context context) {
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index ddcfbf6..5d61f4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -39,10 +39,14 @@
import java.text.NumberFormat;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Default implementation of a {@link BatteryController}. This controller monitors for battery
* level change events that are broadcasted by the system.
*/
+@Singleton
public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController {
private static final String TAG = "BatteryController";
@@ -68,6 +72,7 @@
private Estimate mEstimate;
private long mLastEstimateTimestamp = -1;
+ @Inject
public BatteryControllerImpl(Context context) {
this(context, context.getSystemService(PowerManager.class));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 6f64a563..c855000 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -42,6 +44,13 @@
import java.util.List;
import java.util.WeakHashMap;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener {
private static final String TAG = "BluetoothController";
@@ -61,7 +70,10 @@
private final H mHandler = new H(Looper.getMainLooper());
private int mState;
- public BluetoothControllerImpl(Context context, Looper bgLooper) {
+ /**
+ */
+ @Inject
+ public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class);
mBgHandler = new Handler(bgLooper);
if (mLocalBluetoothManager != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index ccfe073..c7d337a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -44,8 +44,12 @@
import java.util.Set;
import java.util.UUID;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/** Platform implementation of the cast controller. **/
+@Singleton
public class CastControllerImpl implements CastController {
private static final String TAG = "CastController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -63,6 +67,7 @@
private boolean mCallbackRegistered;
private MediaProjectionInfo mProjection;
+ @Inject
public CastControllerImpl(Context context) {
mContext = context;
mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
index f2283a5..c995162 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -27,6 +27,12 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements
DeviceProvisionedController {
@@ -36,6 +42,9 @@
private final Uri mDeviceProvisionedUri;
private final Uri mUserSetupUri;
+ /**
+ */
+ @Inject
public DeviceProvisionedControllerImpl(Context context) {
super(context);
mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index a6146a6..fd030d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -19,7 +19,6 @@
import android.os.Handler;
import android.util.ArrayMap;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
@@ -34,6 +33,12 @@
import java.util.function.Consumer;
import java.util.function.Supplier;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class ExtensionControllerImpl implements ExtensionController {
public static final int SORT_ORDER_PLUGIN = 0;
@@ -43,9 +48,24 @@
public static final int SORT_ORDER_DEFAULT = 4;
private final Context mDefaultContext;
+ private final LeakDetector mLeakDetector;
+ private final PluginManager mPluginManager;
+ private final TunerService mTunerService;
+ private final ConfigurationController mConfigurationController;
- public ExtensionControllerImpl(Context context) {
+ /**
+ */
+ @Inject
+ public ExtensionControllerImpl(Context context,
+ LeakDetector leakDetector,
+ PluginManager pluginManager,
+ TunerService tunerService,
+ ConfigurationController configurationController) {
mDefaultContext = context;
+ mLeakDetector = leakDetector;
+ mPluginManager = pluginManager;
+ mTunerService = tunerService;
+ mConfigurationController = configurationController;
}
@Override
@@ -159,14 +179,14 @@
@Override
public void clearItem(boolean isDestroyed) {
if (isDestroyed && mItem != null) {
- Dependency.get(LeakDetector.class).trackGarbage(mItem);
+ mLeakDetector.trackGarbage(mItem);
}
mItem = null;
}
private void notifyChanged() {
if (mItem != null) {
- Dependency.get(LeakDetector.class).trackGarbage(mItem);
+ mLeakDetector.trackGarbage(mItem);
}
mItem = null;
for (int i = 0; i < mProducers.size(); i++) {
@@ -207,7 +227,7 @@
public PluginItem(String action, Class<P> cls, PluginConverter<T, P> converter) {
mConverter = converter;
- Dependency.get(PluginManager.class).addPluginListener(action, this, cls);
+ mPluginManager.addPluginListener(action, this, cls);
}
@Override
@@ -235,7 +255,7 @@
@Override
public void destroy() {
- Dependency.get(PluginManager.class).removePluginListener(this);
+ mPluginManager.removePluginListener(this);
}
@Override
@@ -251,7 +271,7 @@
public TunerItem(TunerFactory<T> factory, String... setting) {
mFactory = factory;
- Dependency.get(TunerService.class).addTunable(this, setting);
+ mTunerService.addTunable(this, setting);
}
@Override
@@ -261,7 +281,7 @@
@Override
public void destroy() {
- Dependency.get(TunerService.class).removeTunable(this);
+ mTunerService.removeTunable(this);
}
@Override
@@ -289,7 +309,7 @@
mSupplier = supplier;
mUiMode = mDefaultContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_TYPE_MASK;
- Dependency.get(ConfigurationController.class).addCallback(this);
+ mConfigurationController.addCallback(this);
}
@Override
@@ -309,7 +329,7 @@
@Override
public void destroy() {
- Dependency.get(ConfigurationController.class).removeCallback(this);
+ mConfigurationController.removeCallback(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index e23ce2d..41ff9d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -35,9 +35,13 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manages the flashlight.
*/
+@Singleton
public class FlashlightControllerImpl implements FlashlightController {
private static final String TAG = "FlashlightController";
@@ -64,6 +68,7 @@
private String mCameraId;
private boolean mTorchAvailable;
+ @Inject
public FlashlightControllerImpl(Context context) {
mContext = context;
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index e7280643..a02c9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,8 +16,7 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,7 +31,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 0a72c3f..420abe8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -29,6 +29,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
private static final String TAG = "HotspotController";
@@ -43,6 +49,9 @@
private int mNumConnectedDevices;
private boolean mWaitingForTerminalState;
+ /**
+ */
+ @Inject
public HotspotControllerImpl(Context context) {
mContext = context;
mConnectivityManager =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
index aee021c..ba6369e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java
@@ -18,6 +18,7 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_ICONS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.STATUS_BAR_ICONS_CHANGED;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
import android.content.Context;
import android.metrics.LogMaker;
@@ -32,6 +33,9 @@
import java.util.Arrays;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
+
public class IconLoggerImpl implements IconLogger {
// Minimum ms between log statements.
@@ -46,7 +50,9 @@
private final List<String> mIconIndex;
private long mLastLog = System.currentTimeMillis();
- public IconLoggerImpl(Context context, Looper bgLooper, MetricsLogger logger) {
+ @Inject
+ public IconLoggerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+ MetricsLogger logger) {
mContext = context;
mHandler = new Handler(bgLooper);
mLogger = logger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index 5eb0fb7..3c8ed6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -25,6 +25,12 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback
implements KeyguardMonitor {
@@ -47,6 +53,9 @@
private boolean mKeyguardGoingAway;
private boolean mLaunchTransitionFadingAway;
+ /**
+ */
+ @Inject
public KeyguardMonitorImpl(Context context) {
mContext = context;
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 840e77e..683cdbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import static com.android.settingslib.Utils.updateLocationEnabled;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -40,9 +41,14 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/**
* A controller to manage changes of location related states and update the views accordingly.
*/
+@Singleton
public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
private static final int[] mHighPowerRequestAppOpArray
@@ -59,7 +65,8 @@
new ArrayList<LocationChangeCallback>();
private final H mHandler = new H();
- public LocationControllerImpl(Context context, Looper bgLooper) {
+ @Inject
+ public LocationControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
mContext = context;
// Register to listen for changes in location settings.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index bc43120..9422101 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -22,6 +22,8 @@
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -65,7 +67,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
@@ -74,7 +75,12 @@
import java.util.Locale;
import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/** Platform implementation of the network controller. **/
+@Singleton
public class NetworkControllerImpl extends BroadcastReceiver
implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
ConfigurationChangedReceiver, Dumpable {
@@ -154,7 +160,8 @@
/**
* Construct this controller object and register for updates.
*/
- public NetworkControllerImpl(Context context, Looper bgLooper,
+ @Inject
+ public NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
DeviceProvisionedController deviceProvisionedController) {
this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index dac878c..288b3af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -27,6 +27,13 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Implementation of {@link NextAlarmController}
+ */
+@Singleton
public class NextAlarmControllerImpl extends BroadcastReceiver
implements NextAlarmController {
@@ -35,6 +42,9 @@
private AlarmManager mAlarmManager;
private AlarmManager.AlarmClockInfo mNextAlarm;
+ /**
+ */
+ @Inject
public NextAlarmControllerImpl(Context context) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
IntentFilter filter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
index c2933e1..2a10db6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -27,9 +27,13 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBar;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Let {@link RemoteInputView} to control the visibility of QuickSetting.
*/
+@Singleton
public class RemoteInputQuickSettingsDisabler
implements ConfigurationController.ConfigurationListener {
@@ -39,6 +43,7 @@
private int mLastOrientation;
@VisibleForTesting CommandQueue mCommandQueue;
+ @Inject
public RemoteInputQuickSettingsDisabler(Context context) {
mContext = context;
mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 5418dc1..1f368e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -23,7 +23,11 @@
import java.util.concurrent.CopyOnWriteArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/** Platform implementation of the rotation lock controller. **/
+@Singleton
public final class RotationLockControllerImpl implements RotationLockController {
private final Context mContext;
private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
@@ -37,6 +41,7 @@
}
};
+ @Inject
public RotationLockControllerImpl(Context context) {
mContext = context;
setListening(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 29a6b95..e0259c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -55,6 +55,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class SecurityControllerImpl extends CurrentUserTracker implements SecurityController {
private static final String TAG = "SecurityController";
@@ -90,6 +96,9 @@
// Needs to be cached here since the query has to be asynchronous
private ArrayMap<Integer, Boolean> mHasCACerts = new ArrayMap<Integer, Boolean>();
+ /**
+ */
+ @Inject
public SecurityControllerImpl(Context context) {
this(context, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 71d6e54..6193159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -27,6 +29,11 @@
import com.android.systemui.R;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Singleton
public final class SmartReplyConstants extends ContentObserver {
private static final String TAG = "SmartReplyConstants";
@@ -47,7 +54,8 @@
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
- public SmartReplyConstants(Handler handler, Context context) {
+ @Inject
+ public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) {
super(handler);
mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
index fed8032..0ca6ff6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -40,6 +40,12 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class UserInfoControllerImpl implements UserInfoController {
private static final String TAG = "UserInfoController";
@@ -53,6 +59,9 @@
private Drawable mUserDrawable;
private String mUserAccount;
+ /**
+ */
+ @Inject
public UserInfoControllerImpl(Context context) {
mContext = context;
IntentFilter filter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 15b2f2b..e412e09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -68,9 +69,14 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/**
* Keeps a list of all users on the device for user switching.
*/
+@Singleton
public class UserSwitcherController implements Dumpable {
private static final String TAG = "UserSwitcherController";
@@ -102,8 +108,9 @@
private Intent mSecondaryUserServiceIntent;
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
+ @Inject
public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
- Handler handler, ActivityStarter activityStarter) {
+ @Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) {
mContext = context;
mGuestResumeSessionReceiver.register(context);
mKeyguardMonitor = keyguardMonitor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 89ccff0..29c42d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.NotificationManager;
@@ -48,7 +50,12 @@
import java.util.ArrayList;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/** Platform implementation of the zen mode controller. **/
+@Singleton
public class ZenModeControllerImpl extends CurrentUserTracker
implements ZenModeController, Dumpable {
private static final String TAG = "ZenModeController";
@@ -71,7 +78,8 @@
private long mZenUpdateTime;
private NotificationManager.Policy mConsolidatedNotificationPolicy;
- public ZenModeControllerImpl(Context context, Handler handler) {
+ @Inject
+ public ZenModeControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
super(context);
mContext = context;
mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
@@ -109,8 +117,8 @@
@Override
public boolean areNotificationsHiddenInShade() {
if (mZenMode != Global.ZEN_MODE_OFF) {
- return (mConfig.suppressedVisualEffects & NotificationManager.Policy
- .SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+ return (mConsolidatedNotificationPolicy.suppressedVisualEffects
+ & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
}
return false;
}
@@ -252,7 +260,6 @@
}
}
-
@VisibleForTesting
protected void updateZenModeConfig() {
final ZenModeConfig config = mNoMan.getZenModeConfig();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
index e85dee8..81d77a6 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
@@ -21,6 +21,9 @@
import com.android.systemui.Dependency;
import com.android.systemui.tuner.TunerService.Tunable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Version of Space that can be resized by a tunable setting.
*/
@@ -69,8 +72,18 @@
Dependency.get(TunerService.class).removeTunable(this);
}
- // Exists for easy injecting in tests.
+ /**
+ * Exists for easy injecting in tests.
+ */
+ @Singleton
public static class TunablePaddingService {
+
+ /**
+ */
+ @Inject
+ public TunablePaddingService() {
+ }
+
public TunablePadding add(View view, String key, int defaultSize, int flags) {
if (view == null) {
throw new IllegalArgumentException();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 196d9bc..0a47f19 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -43,7 +43,13 @@
import java.util.HashSet;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class TunerServiceImpl extends TunerService {
private static final String TUNER_VERSION = "sysui_tuner_version";
@@ -70,6 +76,9 @@
private int mCurrentUser;
private CurrentUserTracker mUserTracker;
+ /**
+ */
+ @Inject
public TunerServiceImpl(Context context) {
mContext = context;
mContentResolver = mContext.getContentResolver();
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
index 88cbbb5..31f4991 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
@@ -38,6 +38,9 @@
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Wrapper around sensor manager that hides potential sources of latency.
*
@@ -45,6 +48,7 @@
* without blocking. Note that this means registering listeners now always appears successful even
* if it is not.
*/
+@Singleton
public class AsyncSensorManager extends SensorManager
implements PluginListener<SensorManagerPlugin> {
@@ -56,8 +60,14 @@
@VisibleForTesting final Handler mHandler;
private final List<SensorManagerPlugin> mPlugins;
- public AsyncSensorManager(SensorManager inner, PluginManager pluginManager) {
- mInner = inner;
+ @Inject
+ public AsyncSensorManager(Context context, PluginManager pluginManager) {
+ this(context.getSystemService(SensorManager.class), pluginManager);
+ }
+
+ @VisibleForTesting
+ AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
+ mInner = sensorManager;
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
new file mode 100644
index 0000000..e458e63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -0,0 +1,167 @@
+/*
+ * 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.util;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QuickStatusBarHeader;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Manages inflation that requires dagger injection.
+ * See docs/dagger.md for details.
+ */
+@Singleton
+public class InjectionInflationController {
+
+ public static final String VIEW_CONTEXT = "view_context";
+ private final ViewCreator mViewCreator;
+ private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
+ private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
+
+ @Inject
+ public InjectionInflationController(SystemUIFactory.SystemUIRootComponent rootComponent) {
+ mViewCreator = rootComponent.createViewCreator();
+ initInjectionMap();
+ }
+
+ ArrayMap<String, Method> getInjectionMap() {
+ return mInjectionMap;
+ }
+
+ ViewCreator getFragmentCreator() {
+ return mViewCreator;
+ }
+
+ /**
+ * Wraps a {@link LayoutInflater} to support creating dagger injected views.
+ * See docs/dagger.md for details.
+ */
+ public LayoutInflater injectable(LayoutInflater inflater) {
+ LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
+ ret.setPrivateFactory(mFactory);
+ return ret;
+ }
+
+ private void initInjectionMap() {
+ for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
+ if (View.class.isAssignableFrom(method.getReturnType())
+ && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+ mInjectionMap.put(method.getReturnType().getName(), method);
+ }
+ }
+ }
+
+ /**
+ * The subcomponent of dagger that holds all views that need injection.
+ */
+ @Subcomponent
+ public interface ViewCreator {
+ /**
+ * Creates another subcomponent to actually generate the view.
+ */
+ ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider);
+ }
+
+ /**
+ * Secondary sub-component that actually creates the views.
+ *
+ * Having two subcomponents lets us hide the complexity of providing the named context
+ * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that
+ * creates a new ViewInstanceCreator any time we need to inflate a view.
+ */
+ @Subcomponent(modules = ViewAttributeProvider.class)
+ public interface ViewInstanceCreator {
+ /**
+ * Creates the QuickStatusBarHeader.
+ */
+ QuickStatusBarHeader createQsHeader();
+ }
+
+ /**
+ * Module for providing view-specific constructor objects.
+ */
+ @Module
+ public class ViewAttributeProvider {
+ private final Context mContext;
+ private final AttributeSet mAttrs;
+
+ private ViewAttributeProvider(Context context, AttributeSet attrs) {
+ mContext = context;
+ mAttrs = attrs;
+ }
+
+ /**
+ * Provides the view-themed context (as opposed to the global sysui application context).
+ */
+ @Provides
+ @Named(VIEW_CONTEXT)
+ public Context provideContext() {
+ return mContext;
+ }
+
+ /**
+ * Provides the AttributeSet for the current view being inflated.
+ */
+ @Provides
+ public AttributeSet provideAttributeSet() {
+ return mAttrs;
+ }
+ }
+
+ private class InjectionFactory implements LayoutInflater.Factory2 {
+
+ @Override
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ Method creationMethod = mInjectionMap.get(name);
+ if (creationMethod != null) {
+ ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs);
+ try {
+ return (View) creationMethod.invoke(
+ mViewCreator.createInstanceCreator(provider));
+ } catch (IllegalAccessException e) {
+ throw new InflateException("Could not inflate " + name, e);
+ } catch (InvocationTargetException e) {
+ throw new InflateException("Could not inflate " + name, e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ return onCreateView(name, context, attrs);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index b2cc269..7bc9626 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -17,6 +17,7 @@
package com.android.systemui.util.leak;
import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
import android.app.ActivityManager;
import android.content.Context;
@@ -51,6 +52,13 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
public class GarbageMonitor {
private static final boolean LEAK_REPORTING_ENABLED =
Build.IS_DEBUGGABLE
@@ -85,9 +93,12 @@
private long mHeapLimit;
+ /**
+ */
+ @Inject
public GarbageMonitor(
Context context,
- Looper bgLooper,
+ @Named(BG_LOOPER_NAME) Looper bgLooper,
LeakDetector leakDetector,
LeakReporter leakReporter) {
mContext = context.getApplicationContext();
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
index a47e99d..b25df5f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
@@ -16,6 +16,9 @@
package com.android.systemui.util.leak;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -39,9 +42,14 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/**
* Dumps data to debug leaks and posts a notification to share the data.
*/
+@Singleton
public class LeakReporter {
static final String TAG = "LeakReporter";
@@ -56,7 +64,9 @@
private final LeakDetector mLeakDetector;
private final String mLeakReportEmail;
- public LeakReporter(Context context, LeakDetector leakDetector, String leakReportEmail) {
+ @Inject
+ public LeakReporter(Context context, LeakDetector leakDetector,
+ @Nullable @Named(LEAK_REPORT_EMAIL_NAME) String leakReportEmail) {
mContext = context;
mLeakDetector = leakDetector;
mLeakReportEmail = leakReportEmail;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index e20e267..32bc01c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -67,6 +67,9 @@
import java.util.Map;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Source of truth for all state / events related to the volume dialog. No presentation.
*
@@ -74,6 +77,7 @@
*
* Methods ending in "W" must be called on the worker thread.
*/
+@Singleton
public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
@@ -131,6 +135,7 @@
protected final VC mVolumeController = new VC();
+ @Inject
public VolumeDialogControllerImpl(Context context) {
mContext = context.getApplicationContext();
mNotificationManager = (NotificationManager) mContext.getSystemService(
@@ -545,7 +550,8 @@
}
private boolean updateZenConfig() {
- final NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
+ final NotificationManager.Policy policy =
+ mNotificationManager.getConsolidatedNotificationPolicy();
boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_ALARMS) == 0;
boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index fb2ceac..4150602 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -107,6 +107,25 @@
}
@Test
+ public void onPluginConnected_showPluginBigClock() {
+ // GIVEN that the container for the big clock has visibility GONE
+ FrameLayout bigClockContainer = new FrameLayout(getContext());
+ bigClockContainer.setVisibility(GONE);
+ mKeyguardClockSwitch.setBigClockContainer(bigClockContainer);
+ // AND the plugin returns a view for the big clock
+ ClockPlugin plugin = mock(ClockPlugin.class);
+ TextClock pluginView = new TextClock(getContext());
+ when(plugin.getBigClockView()).thenReturn(pluginView);
+ PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+ // WHEN the plugin is connected
+ listener.onPluginConnected(plugin, null);
+ // THEN the big clock container is visible and it is the parent of the
+ // big clock view.
+ assertThat(bigClockContainer.getVisibility()).isEqualTo(VISIBLE);
+ assertThat(pluginView.getParent()).isEqualTo(bigClockContainer);
+ }
+
+ @Test
public void onPluginConnected_nullView() {
ClockPlugin plugin = mock(ClockPlugin.class);
PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
@@ -146,6 +165,26 @@
}
@Test
+ public void onPluginDisconnected_hidePluginBigClock() {
+ // GIVEN that the big clock container is visible
+ FrameLayout bigClockContainer = new FrameLayout(getContext());
+ bigClockContainer.setVisibility(VISIBLE);
+ mKeyguardClockSwitch.setBigClockContainer(bigClockContainer);
+ // AND the plugin returns a view for the big clock
+ ClockPlugin plugin = mock(ClockPlugin.class);
+ TextClock pluginView = new TextClock(getContext());
+ when(plugin.getBigClockView()).thenReturn(pluginView);
+ PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+ listener.onPluginConnected(plugin, null);
+ // WHEN the plugin is disconnected
+ listener.onPluginDisconnected(plugin);
+ // THEN the big lock container is GONE and the big clock view doesn't have
+ // a parent.
+ assertThat(bigClockContainer.getVisibility()).isEqualTo(GONE);
+ assertThat(pluginView.getParent()).isNull();
+ }
+
+ @Test
public void onPluginDisconnected_nullView() {
ClockPlugin plugin = mock(ClockPlugin.class);
PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 0c8d137..18bf75e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -29,9 +29,10 @@
mComponents = ((SysuiTestableContext) context).getComponents();
}
mContext = context;
- if (SystemUIFactory.getInstance() == null) {
- SystemUIFactory.createFromConfig(context);
- }
+ SystemUIFactory.createFromConfig(context);
+ SystemUIFactory.getInstance().getRootComponent()
+ .createDependency()
+ .createSystemUI(this);
start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index b699163..2582946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -17,8 +17,10 @@
package com.android.systemui.appops;
import static org.junit.Assert.assertEquals;
+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.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -32,7 +34,6 @@
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationPresenter;
import org.junit.Before;
import org.junit.Test;
@@ -48,9 +49,12 @@
private static final int TEST_UID = 0;
private static final int TEST_UID_OTHER = 500000;
- @Mock private NotificationPresenter mPresenter;
- @Mock private AppOpsManager mAppOpsManager;
- @Mock private AppOpsController.Callback mCallback;
+ @Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
+ private AppOpsController.Callback mCallback;
+ @Mock
+ private AppOpsControllerImpl.H mMockHandler;
private AppOpsControllerImpl mController;
@@ -77,9 +81,13 @@
@Test
public void addCallback_includedCode() {
- mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mController.addCallback(
+ new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
+ mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
}
@@ -106,7 +114,7 @@
@Test
public void addCallback_notSameCode() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
- mController.removeCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
@@ -128,17 +136,30 @@
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
TEST_UID, TEST_PACKAGE_NAME, true);
- assertEquals(2, mController.getActiveAppOps().size());
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
+ TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+ assertEquals(3, mController.getActiveAppOps().size());
}
- @Test public void getActiveItemsForUser() {
+ @Test
+ public void getActiveItemsForUser() {
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
- assertEquals(1,
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
+ TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
+ assertEquals(2,
mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
assertEquals(1,
- mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
+ mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size());
+ }
+
+ @Test
+ public void opNotedScheduledForRemoval() {
+ mController.setBGHandler(mMockHandler);
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
+ verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 6abd407..f3cb27f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -19,8 +19,6 @@
import android.annotation.NonNull;
import android.app.PendingIntent;
-import com.android.systemui.util.wakelock.WakeLock;
-
/**
* A rudimentary fake for DozeHost.
*/
@@ -85,11 +83,6 @@
}
@Override
- public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
- throw new RuntimeException("not implemented");
- }
-
- @Override
public void onIgnoreTouchWhilePulsing(boolean ignore) {
}
@@ -115,11 +108,6 @@
}
@Override
- public void setPassiveInterrupt(boolean lightInterrupt) {
-
- }
-
- @Override
public void setDozeScreenBrightness(int value) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index c1c80ce..a8ff0b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -19,13 +19,14 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.AlarmManager;
import android.content.ContentResolver;
-import android.content.Intent;
+import android.media.MediaMetadata;
import android.net.Uri;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
@@ -40,7 +41,9 @@
import androidx.slice.builders.ListBuilder;
import androidx.slice.core.SliceQuery;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationMediaManager;
import org.junit.Assert;
import org.junit.Before;
@@ -62,6 +65,8 @@
private ContentResolver mContentResolver;
@Mock
private AlarmManager mAlarmManager;
+ @Mock
+ private NotificationMediaManager mNotificationMediaManager;
private TestableKeyguardSliceProvider mProvider;
private boolean mIsZenMode;
@@ -71,6 +76,7 @@
mIsZenMode = false;
mProvider = new TestableKeyguardSliceProvider();
mProvider.attachInfo(getContext(), null);
+ mProvider.initDependencies();
SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
}
@@ -90,8 +96,18 @@
}
@Test
+ public void onBindSlice_readsMedia() {
+ MediaMetadata metadata = mock(MediaMetadata.class);
+ mProvider.onMetadataChanged(metadata);
+ mProvider.onBindSlice(mProvider.getUri());
+ verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
+ verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST));
+ verify(mNotificationMediaManager).getMediaIcon();
+ }
+
+ @Test
public void cleansDateFormat() {
- mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIMEZONE_CHANGED));
+ mProvider.mKeyguardUpdateMonitorCallback.onTimeZoneChanged(null);
TestableLooper.get(this).processAllMessages();
Assert.assertEquals("Date format should have been cleaned.", 1 /* expected */,
mProvider.mCleanDateFormatInvokations);
@@ -99,7 +115,7 @@
@Test
public void updatesClock() {
- mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK));
+ mProvider.mKeyguardUpdateMonitorCallback.onTimeChanged();
TestableLooper.get(this).processAllMessages();
verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
}
@@ -135,14 +151,20 @@
public void addZenMode_addedToSlice() {
ListBuilder listBuilder = spy(new ListBuilder(getContext(), mProvider.getUri(),
ListBuilder.INFINITY));
- mProvider.addZenMode(listBuilder);
+ mProvider.addZenModeLocked(listBuilder);
verify(listBuilder, never()).addRow(any(ListBuilder.RowBuilder.class));
mIsZenMode = true;
- mProvider.addZenMode(listBuilder);
+ mProvider.addZenModeLocked(listBuilder);
verify(listBuilder).addRow(any(ListBuilder.RowBuilder.class));
}
+ @Test
+ public void onMetadataChanged_updatesSlice() {
+ mProvider.onMetadataChanged(mock(MediaMetadata.class));
+ verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+ }
+
private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
int mCleanDateFormatInvokations;
private int mCounter;
@@ -165,14 +187,24 @@
}
@Override
- void cleanDateFormat() {
- super.cleanDateFormat();
+ void cleanDateFormatLocked() {
+ super.cleanDateFormatLocked();
mCleanDateFormatInvokations++;
}
@Override
- protected String getFormattedDate() {
- return super.getFormattedDate() + mCounter++;
+ public KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
+ return mock(KeyguardUpdateMonitor.class);
+ }
+
+ @Override
+ protected String getFormattedDateLocked() {
+ return super.getFormattedDateLocked() + mCounter++;
+ }
+
+ @Override
+ public void initDependencies() {
+ mMediaManager = mNotificationMediaManager;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
index b23f667..d3b3dae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
@@ -51,4 +51,19 @@
assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[2])
}
+
+ @Test
+ fun testOrder() {
+ // We want location to always go last, so it will go in the "+ other apps"
+ val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA, PrivacyApplication("Camera", context))
+ val appMicrophone =
+ PrivacyItem(PrivacyType.TYPE_MICROPHONE, PrivacyApplication("Microphone", context))
+ val appLocation =
+ PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("Location", context))
+
+ val items = listOf(appLocation, appMicrophone, appCamera)
+ val textBuilder = PrivacyDialogBuilder(context, items)
+ val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
+ assertEquals(listOf("Camera", "Microphone", "Location"), appList)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 24bcca50..e6d7ee7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -19,6 +19,7 @@
import android.app.ActivityManager
import android.app.AppOpsManager
import android.content.Intent
+import android.content.pm.UserInfo
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
@@ -27,16 +28,20 @@
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import com.android.systemui.Dependency
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
@@ -52,8 +57,12 @@
companion object {
val CURRENT_USER_ID = ActivityManager.getCurrentUser()
- val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
+ val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
+ const val SYSTEM_UID = 1000
+ const val TEST_PACKAGE_NAME = "test"
+ const val DEVICE_SERVICES_STRING = "Device services"
const val TAG = "PrivacyItemControllerTest"
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
}
@Mock
@@ -62,6 +71,8 @@
private lateinit var callback: PrivacyItemController.Callback
@Mock
private lateinit var userManager: UserManager
+ @Captor
+ private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
private lateinit var testableLooper: TestableLooper
private lateinit var privacyItemController: PrivacyItemController
@@ -75,9 +86,14 @@
mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
mContext.addMockSystemService(UserManager::class.java, userManager)
+ mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
+ DEVICE_SERVICES_STRING)
- doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ doReturn(listOf(object : UserInfo() {
+ init {
+ id = CURRENT_USER_ID
+ }
+ })).`when`(userManager).getProfiles(anyInt())
privacyItemController = PrivacyItemController(mContext, callback)
}
@@ -100,6 +116,30 @@
}
@Test
+ fun testDistinctItems() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.setListening(true)
+ testableLooper.processAllMessages()
+ verify(callback).privacyChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ }
+
+ @Test
+ fun testSystemApps() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
+ 0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ privacyItemController.setListening(true)
+ testableLooper.processAllMessages()
+ verify(callback).privacyChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ assertEquals(context.getString(R.string.device_services),
+ argCaptor.value[0].application.applicationName)
+ }
+
+ @Test
fun testRegisterReceiver_allUsers() {
val spiedContext = spy(mContext)
val itemController = PrivacyItemController(spiedContext, callback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index bc7d983..45e49df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -18,7 +18,9 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import android.app.Fragment;
import android.content.Context;
+import android.os.Bundle;
import android.os.Looper;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -32,10 +34,13 @@
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Ignore;
@@ -52,6 +57,7 @@
public QSFragmentTest() {
super(QSFragment.class);
+ injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
}
@Before
@@ -70,7 +76,6 @@
mDependency.injectTestDependency(Dependency.BG_LOOPER,
TestableLooper.get(this).getLooper());
mDependency.injectMockDependency(UserSwitcherController.class);
- injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
}
@Test
@@ -116,4 +121,11 @@
assertTrue(qs.isListening());
assertTrue(qs.isExpanded());
}
+
+ @Override
+ protected Fragment instantiate(Context context, String className, Bundle arguments) {
+ return new QSFragment(new RemoteInputQuickSettingsDisabler(context),
+ new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+ context);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 26fa20d..c3a3e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -218,4 +218,12 @@
}
assertFalse(specs.contains("other"));
}
+
+ @Test
+ public void testQueryTiles_nullSetting() {
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+ mTileQueryHelper.queryTiles(mQSTileHost);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index f8ff583..894ef3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -26,6 +26,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -55,7 +56,8 @@
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationListContainer mListContainer;
- @Mock private NotificationEntryManager.Callback mEntryManagerCallback;
+ @Mock
+ private NotificationEntryListener mEntryManagerCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 520a927..8cf4b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -35,6 +36,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
@@ -90,7 +92,10 @@
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
- mViewHierarchyManager = new NotificationViewHierarchyManager(mContext);
+ mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
+ mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+ mock(StatusBarStateController.class), mEntryManager, mock(BubbleController.class),
+ () -> mShadeController);
Dependency.get(InitController.class).executePostInitTasks();
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index def7513..871ff89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -29,8 +29,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -50,7 +48,6 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -105,6 +102,7 @@
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+ when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification);
when(mMockPackageManager.checkUidPermission(
eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
@@ -129,41 +127,6 @@
}
@Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
- initStatusBarNotification(false);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertFalse(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
- initStatusBarNotification(true);
-
- assertFalse(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
- initStatusBarNotification(true);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertTrue(
- NotificationData.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
public void testChannelSetWhenAdded() {
mNotificationData.add(mRow.getEntry());
assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel);
@@ -230,76 +193,6 @@
}
@Test
- public void testSuppressSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
- sbn.getNotification().extras = bundle;
-
- assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressSystemAlertNotification() {
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
- sbn.getNotification().extras = bundle;
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testDoNotSuppressMalformedSystemAlertNotification() {
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-
- // missing extra
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
- StatusBarNotification sbn = mRow.getEntry().notification;
- Bundle bundle = new Bundle();
- bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
- sbn.getNotification().extras = bundle;
-
- // extra missing values
- assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
- }
-
- @Test
- public void testShouldFilterHiddenNotifications() {
- initStatusBarNotification(false);
- // setup
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
- when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
- // test should filter out hidden notifications:
- // hidden
- when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
- assertTrue(mNotificationData.shouldFilterOut(entry));
-
- // not hidden
- when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
- entry = new NotificationData.Entry(mMockStatusBarNotification);
- assertFalse(mNotificationData.shouldFilterOut(entry));
- }
-
- @Test
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
throws Exception {
mNotificationData.add(mRow.getEntry());
@@ -325,9 +218,10 @@
Notification n = mMockStatusBarNotification.getNotification();
n.flags = Notification.FLAG_FOREGROUND_SERVICE;
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -341,9 +235,10 @@
n = nb.build();
when(mMockStatusBarNotification.getNotification()).thenReturn(n);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -353,9 +248,10 @@
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
+ mNotificationData.add(entry);
- assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+ assertTrue(entry.isExemptFromDndVisualSuppression());
+ assertFalse(entry.shouldSuppressAmbient());
}
@Test
@@ -365,31 +261,33 @@
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
+ mNotificationData.add(entry);
+
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
- assertTrue(mNotificationData.shouldSuppressAmbient(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
+ assertTrue(entry.shouldSuppressAmbient());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
when(mMockStatusBarNotification.getNotification()).thenReturn(
new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
- assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+ assertFalse(entry.isExemptFromDndVisualSuppression());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 7f0e435..701ea7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -103,7 +103,10 @@
@Mock private KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationListContainer mListContainer;
- @Mock private NotificationEntryManager.Callback mCallback;
+ @Mock
+ private NotificationEntryListener mCallback;
+ @Mock
+ private NotificationRowBinder.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotificationListenerService.RankingMap mRankingMap;
@Mock private RemoteInputController mRemoteInputController;
@@ -135,7 +138,6 @@
super(context);
mBarService = barService;
mCountDownLatch = new CountDownLatch(1);
- mUseHeadsUp = true;
}
@Override
@@ -231,7 +233,11 @@
mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
- mEntryManager.setNotificationClicker(mock(NotificationClicker.class));
+
+ NotificationRowBinder notificationRowBinder = Dependency.get(NotificationRowBinder.class);
+ notificationRowBinder.setUpWithPresenter(
+ mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
+ notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@@ -244,7 +250,7 @@
doAnswer(invocation -> {
mCountDownLatch.countDown();
return null;
- }).when(mCallback).onBindRow(any(), any(), any(), any());
+ }).when(mBindCallback).onBindRow(any(), any(), any(), any());
// Post on main thread, otherwise we will be stuck waiting here for the inflation finished
// callback forever, since it won't execute until the tests ends.
@@ -261,7 +267,7 @@
// Row inflation:
ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass(
NotificationData.Entry.class);
- verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
+ verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
NotificationData.Entry entry = entryCaptor.getValue();
verify(mRemoteInputManager).bindRow(entry.getRow());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
new file mode 100644
index 0000000..da8bc01d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.notification;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class NotificationFilterTest extends SysuiTestCase {
+
+ private static final int UID_NORMAL = 123;
+ private static final int UID_ALLOW_DURING_SETUP = 456;
+ private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+
+ private final StatusBarNotification mMockStatusBarNotification =
+ mock(StatusBarNotification.class);
+
+ @Mock
+ ForegroundServiceController mFsc;
+ @Mock
+ NotificationData.KeyguardEnvironment mEnvironment;
+ private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
+
+ private NotificationFilter mNotificationFilter;
+ private ExpandableNotificationRow mRow;
+
+ @Before
+ public void setUp() throws Exception {
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ MockitoAnnotations.initMocks(this);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+
+ when(mMockPackageManager.checkUidPermission(
+ eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+ eq(UID_NORMAL)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockPackageManager.checkUidPermission(
+ eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+ eq(UID_ALLOW_DURING_SETUP)))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+ mDependency.injectTestDependency(NotificationGroupManager.class,
+ new NotificationGroupManager());
+ mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
+ when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+ when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+ mRow = new NotificationTestHelper(getContext()).createRow();
+ mNotificationFilter = new NotificationFilter();
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
+ initStatusBarNotification(false);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+ assertFalse(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
+ initStatusBarNotification(true);
+
+ assertFalse(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
+ initStatusBarNotification(true);
+ when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+ assertTrue(
+ NotificationFilter.showNotificationEvenIfUnprovisioned(
+ mMockPackageManager,
+ mMockStatusBarNotification));
+ }
+
+ @Test
+ public void testSuppressSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+ sbn.getNotification().extras = bundle;
+
+ assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testDoNotSuppressSystemAlertNotification() {
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+ sbn.getNotification().extras = bundle;
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testDoNotSuppressMalformedSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+
+ // missing extra
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{});
+ sbn.getNotification().extras = bundle;
+
+ // extra missing values
+ assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+ }
+
+ @Test
+ public void testShouldFilterHiddenNotifications() {
+ initStatusBarNotification(false);
+ // setup
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+ when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+ // test should filter out hidden notifications:
+ // hidden
+ NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ entry.suspended = true;
+ assertTrue(mNotificationFilter.shouldFilterOut(entry));
+
+ // not hidden
+ entry = new NotificationData.Entry(mMockStatusBarNotification);
+ entry.suspended = false;
+ assertFalse(mNotificationFilter.shouldFilterOut(entry));
+ }
+
+ private void initStatusBarNotification(boolean allowDuringSetup) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
+ Notification notification = new Notification.Builder(mContext, "test")
+ .addExtras(bundle)
+ .build();
+ when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index e65e806..b4f99c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -126,7 +126,7 @@
mPowerManager = new PowerManager(mContext, powerManagerService,
Handler.createAsync(Looper.myLooper()));
- mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
+ mEntryManager = new TestableNotificationEntryManager(mPowerManager,
mContext);
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
Dependency.get(InitController.class).executePostInitTasks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 9e2db91..728723b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,10 +14,13 @@
package com.android.systemui.statusbar.phone;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.Fragment;
import android.content.Context;
+import android.os.Bundle;
import android.os.Looper;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -27,13 +30,17 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +51,23 @@
@SmallTest
public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
+ private OverviewProxyService mOverviewProxyService =
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ private AccessibilityManagerWrapper mAccessibilityWrapper =
+ new AccessibilityManagerWrapper(mContext) {
+ Tracker mTracker = mLeakCheck.getTracker("accessibility_manager");
+
+ @Override
+ public void addCallback(AccessibilityServicesStateChangeListener listener) {
+ mTracker.getLeakInfo(listener).addAllocation(new Throwable());
+ }
+
+ @Override
+ public void removeCallback(AccessibilityServicesStateChangeListener listener) {
+ mTracker.getLeakInfo(listener).clearAllocations();
+ }
+ };
+
public NavigationBarFragmentTest() {
super(NavigationBarFragment.class);
}
@@ -54,32 +78,19 @@
@Before
public void setup() {
- mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
mSysuiContext.putComponent(Recents.class, mock(Recents.class));
mSysuiContext.putComponent(Divider.class, mock(Divider.class));
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- mDependency.injectMockDependency(OverviewProxyService.class);
WindowManager windowManager = mock(WindowManager.class);
Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
when(windowManager.getDefaultDisplay()).thenReturn(
defaultDisplay);
mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
- Tracker tracker = mLeakCheck.getTracker("accessibility_manager");
- AccessibilityManagerWrapper wrapper = new AccessibilityManagerWrapper(mContext) {
- @Override
- public void addCallback(AccessibilityServicesStateChangeListener listener) {
- tracker.getLeakInfo(listener).addAllocation(new Throwable());
- }
-
- @Override
- public void removeCallback(AccessibilityServicesStateChangeListener listener) {
- tracker.getLeakInfo(listener).clearAllocations();
- }
- };
- mDependency.injectTestDependency(AccessibilityManagerWrapper.class, wrapper);
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+ mDependency.injectTestDependency(AccessibilityManagerWrapper.class, mAccessibilityWrapper);
}
@Test
@@ -91,4 +102,15 @@
navigationBarFragment.onHomeLongClick(navigationBarFragment.getView());
}
+ @Override
+ protected Fragment instantiate(Context context, String className, Bundle arguments) {
+ DeviceProvisionedController deviceProvisionedController =
+ new DeviceProvisionedControllerImpl(context);
+ assertNotNull(mAccessibilityWrapper);
+ return new NavigationBarFragment(mAccessibilityWrapper,
+ deviceProvisionedController,
+ new MetricsLogger(),
+ new AssistManager(deviceProvisionedController, mContext),
+ mOverviewProxyService);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index ee39e10..490288e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -32,9 +32,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -61,8 +61,9 @@
private AmbientPulseManager mAmbientPulseManager;
private HeadsUpManager mHeadsUpManager;
@Mock private NotificationEntryManager mNotificationEntryManager;
- @Captor private ArgumentCaptor<AlertTransferListener> mListenerCaptor;
- private AlertTransferListener mAlertTransferListener;
+ @Captor
+ private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+ private NotificationEntryListener mNotificationEntryListener;
private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
private final NotificationGroupTestHelper mGroupTestHelper =
new NotificationGroupTestHelper(mContext);
@@ -85,8 +86,8 @@
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
- verify(mNotificationEntryManager).setAlertTransferListener(mListenerCaptor.capture());
- mAlertTransferListener = mListenerCaptor.getValue();
+ verify(mNotificationEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
+ mNotificationEntryListener = mListenerCaptor.getValue();
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mAmbientPulseManager.addListener(mGroupAlertTransferHelper);
}
@@ -121,7 +122,7 @@
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// The alert state should transfer back to the summary as there is now more than one
@@ -148,7 +149,7 @@
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// Dozing changed so no reason to re-alert summary.
@@ -186,7 +187,7 @@
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(true);
- mAlertTransferListener.onEntryReinflated(childEntry);
+ mNotificationEntryListener.onEntryReinflated(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
@@ -210,13 +211,13 @@
// Add second child notification so that summary is no longer suppressed.
mPendingEntries.put(childEntry2.key, childEntry2);
- mAlertTransferListener.onPendingEntryAdded(childEntry2);
+ mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// Child entry finishes its inflation.
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(true);
- mAlertTransferListener.onEntryReinflated(childEntry);
+ mNotificationEntryListener.onEntryReinflated(childEntry);
verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
.getContentFlag());
@@ -236,7 +237,7 @@
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mAlertTransferListener.onEntryRemoved(childEntry);
+ mNotificationEntryListener.onEntryRemoved(childEntry);
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e5620a5..c584d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -91,7 +92,10 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -129,6 +133,8 @@
@Mock private ArrayList<Entry> mNotificationList;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
+ @Mock
+ private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
// Mock dependencies:
@Mock private NotificationViewHierarchyManager mViewHierarchyManager;
@@ -141,13 +147,17 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private NotificationPresenter mNotificationPresenter;
- @Mock private NotificationEntryManager.Callback mCallback;
+ @Mock
+ private NotificationEntryListener mCallback;
@Mock private BubbleController mBubbleController;
+ @Mock
+ private NotificationFilter mNotificationFilter;
private TestableStatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
private TestableNotificationEntryManager mEntryManager;
+ private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private NotificationLogger mNotificationLogger;
private CommandQueue mCommandQueue;
@@ -168,6 +178,17 @@
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
+
+ IPowerManager powerManagerService = mock(IPowerManager.class);
+ mPowerManager = new PowerManager(mContext, powerManagerService,
+ Handler.createAsync(Looper.myLooper()));
+
+ mNotificationInterruptionStateProvider =
+ new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+ mDreamManager);
+ mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
+ mNotificationInterruptionStateProvider);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -178,10 +199,6 @@
mNotificationLogger = new NotificationLogger();
DozeLog.traceDozing(mContext, false /* dozing */);
- IPowerManager powerManagerService = mock(IPowerManager.class);
- mPowerManager = new PowerManager(mContext, powerManagerService,
- Handler.createAsync(Looper.myLooper()));
-
mCommandQueue = mock(CommandQueue.class);
when(mCommandQueue.asBinder()).thenReturn(new Binder());
mContext.putComponent(CommandQueue.class, mCommandQueue);
@@ -205,7 +222,10 @@
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext);
+ mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
+ mHeadsUpManager, mHeadsUpSuppressor);
+
+ mEntryManager = new TestableNotificationEntryManager(mPowerManager, mContext);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
@@ -362,11 +382,9 @@
public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -376,19 +394,18 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertTrue(mEntryManager.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -398,46 +415,44 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertFalse(mEntryManager.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
- when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
+ entry.importance = IMPORTANCE_HIGH;
- assertFalse(mEntryManager.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+ when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
- when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
- when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false);
+ when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.importance = IMPORTANCE_HIGH;
- assertTrue(mEntryManager.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -725,20 +740,29 @@
public static class TestableNotificationEntryManager extends NotificationEntryManager {
- public TestableNotificationEntryManager(IDreamManager dreamManager,
- PowerManager powerManager, Context context) {
+ public TestableNotificationEntryManager(PowerManager powerManager, Context context) {
super(context);
- mDreamManager = dreamManager;
mPowerManager = powerManager;
}
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
- Callback callback,
+ NotificationEntryListener callback,
HeadsUpManagerPhone headsUpManager,
NotificationData notificationData) {
super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
+ }
+ }
+
+ public static class TestableNotificationInterruptionStateProvider extends
+ NotificationInterruptionStateProvider {
+
+ public TestableNotificationInterruptionStateProvider(
+ Context context,
+ PowerManager powerManager,
+ IDreamManager dreamManager) {
+ super(context, powerManager, dreamManager);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index 1cceefa..2553ac1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -28,7 +28,6 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.Plugin;
@@ -39,6 +38,7 @@
import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.LeakDetector;
import org.junit.Before;
import org.junit.Test;
@@ -62,7 +62,8 @@
mPluginManager = mDependency.injectMockDependency(PluginManager.class);
mTunerService = mDependency.injectMockDependency(TunerService.class);
mConfigurationController = mDependency.injectMockDependency(ConfigurationController.class);
- mExtensionController = Dependency.get(ExtensionController.class);
+ mExtensionController = new ExtensionControllerImpl(mContext,
+ mock(LeakDetector.class), mPluginManager, mTunerService, mConfigurationController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 7437e834..8f5f072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -89,20 +89,22 @@
@Test
public void testAreNotificationsHiddenInShade_zenOnShadeNotSuppressed() {
- mConfig.suppressedVisualEffects =
- NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
+ NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR);
+ when(mNm.getConsolidatedNotificationPolicy()).thenReturn(policy);
+ mController.updateConsolidatedNotificationPolicy();
mController.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- mController.updateZenModeConfig();
assertFalse(mController.areNotificationsHiddenInShade());
}
@Test
public void testAreNotificationsHiddenInShade_zenOnShadeSuppressed() {
- mConfig.suppressedVisualEffects =
- NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
+ NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ when(mNm.getConsolidatedNotificationPolicy()).thenReturn(policy);
+ mController.updateConsolidatedNotificationPolicy();
mController.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- mController.updateZenModeConfig();
assertTrue(mController.areNotificationsHiddenInShade());
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 529d78f..3180b47 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6664,6 +6664,72 @@
// OS: Q
NOTIFICATION_ACTION_IS_SMART = 1601;
+ // FIELD: true if the associated ACTION_ZEN_ALLOW_* or ACTION_ZEN_BLOCK_* allows/blocks
+ // the effect/sound when DND is on. false if set to disallow/show.
+ // OS: Q
+ FIELD_ZEN_TOGGLE_EXCEPTION = 1602;
+
+ // FIELD: rule id an ACTION_ZEN_ALLOW_* or ACTION_ZEN_BLOCK_* is associated with
+ // OS: Q
+ FIELD_ZEN_RULE_ID = 1603;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_SETTINGS = 1604;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior > Custom
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_SOUND_SETTINGS = 1605;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_DEFAULT_SETTINGS = 1606;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior > Custom
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_CUSTOM_SETTINGS = 1607;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+ // > Notification restriction
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_NOTIFICATION_RESTRICTIONS = 1608;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+ // > Notification restriction > Custom
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_VIS_EFFECTS = 1609;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+ // > Notification restriction > Custom > Allow messages
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_MESSAGES = 1610;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule)
+ // > Do Not Disturb behavior > Use default Do Not Disturb behavior
+ // > Notification restriction > Custom > Allow calls
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_RULE_CALLS = 1611;
+
+ // OPEN: Settings > Sound > Do Not Disturb > Click footer link if custom settings applied
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ZEN_CUSTOM_SETTINGS_DIALOG = 1612;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 612c929..1a6bee9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -74,6 +74,7 @@
import com.android.server.LocalServices;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.SecureSettingsServiceNameResolver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -182,7 +183,9 @@
private int mSupportedSmartSuggestionModes;
public AutofillManagerService(Context context) {
- super(context, UserManager.DISALLOW_AUTOFILL);
+ super(context,
+ new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
+ UserManager.DISALLOW_AUTOFILL);
mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
mAm = LocalServices.getService(ActivityManagerInternal.class);
@@ -523,7 +526,7 @@
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
- service.mAugmentedAutofillResolver.setTemporaryServiceLocked(serviceName,
+ service.mAugmentedAutofillResolver.setTemporaryService(userId, serviceName,
durationMs);
}
}
@@ -535,7 +538,7 @@
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
- service.mAugmentedAutofillResolver.resetTemporaryServiceLocked();
+ service.mAugmentedAutofillResolver.resetTemporaryService(userId);
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index fa62ef8..4b7efe1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -77,7 +77,6 @@
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractPerUserSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
-import com.android.server.infra.SecureSettingsServiceNameResolver;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -170,8 +169,7 @@
AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState, boolean disabled) {
- super(master, new SecureSettingsServiceNameResolver(master.getContext(), userId,
- Settings.Secure.AUTOFILL_SERVICE), lock, userId);
+ super(master, lock, userId);
mRequestsHistory = requestsHistory;
mUiLatencyHistory = uiLatencyHistory;
@@ -181,9 +179,9 @@
mAutofillCompatState = autofillCompatState;
mAugmentedAutofillResolver = new FrameworkResourcesServiceNameResolver(master.getContext(),
- userId, lock, com.android.internal.R.string.config_defaultAugmentedAutofillService);
+ com.android.internal.R.string.config_defaultAugmentedAutofillService);
mAugmentedAutofillResolver.setOnTemporaryServiceNameChangedCallback(
- () -> updateRemoteAugmentedAutofillService());
+ (u, s) -> updateRemoteAugmentedAutofillService());
updateLocked(disabled);
}
@@ -207,7 +205,7 @@
}
@Override // from PerUserSystemService
- protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId);
return mInfo.getServiceInfo();
@@ -873,7 +871,7 @@
pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
.getString(R.string.config_defaultAutofillService));
pw.print(prefix); pw.print("mAugmentedAutofillNamer: ");
- mAugmentedAutofillResolver.dumpShortLocked(pw); pw.println();
+ mAugmentedAutofillResolver.dumpShort(pw); pw.println();
if (mRemoteAugmentedAutofillService != null) {
pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
mRemoteAugmentedAutofillService.dump(prefix2, pw);
@@ -1022,7 +1020,7 @@
@GuardedBy("mLock")
@Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
if (mRemoteAugmentedAutofillService == null) {
- final String serviceName = mAugmentedAutofillResolver.getServiceNameLocked();
+ final String serviceName = mAugmentedAutofillResolver.getServiceName(mUserId);
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): not set");
@@ -1030,7 +1028,7 @@
return null;
}
final ComponentName componentName = RemoteAugmentedAutofillService.getComponentName(
- serviceName, mUserId, mAugmentedAutofillResolver.isTemporaryLocked());
+ serviceName, mUserId, mAugmentedAutofillResolver.isTemporary(mUserId));
if (componentName == null) return null;
if (sVerbose) {
Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
@@ -1053,7 +1051,7 @@
* Called when the {@link #mAugmentedAutofillResolver} changed (among other places).
*/
private void updateRemoteAugmentedAutofillService() {
- final String serviceName = mAugmentedAutofillResolver.getServiceNameLocked();
+ final String serviceName = mAugmentedAutofillResolver.getServiceName(mUserId);
if (serviceName == null) {
if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!");
if (mRemoteAugmentedAutofillService != null) {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index fc7265d..239a386 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -38,8 +38,8 @@
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
+import com.android.internal.infra.AbstractSinglePendingRequestRemoteService;
import com.android.internal.os.IResultReceiver;
-import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
final class RemoteAugmentedAutofillService
extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService,
@@ -47,8 +47,6 @@
private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
- // TODO(b/117779333): changed it so it's permanentely bound
- private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
@@ -90,7 +88,7 @@
@Override // from AbstractRemoteService
protected long getTimeoutIdleBindMillis() {
- return TIMEOUT_IDLE_BIND_MILLIS;
+ return PERMANENT_BOUND_TIMEOUT_MS;
}
@Override // from AbstractRemoteService
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 417ea9c..34fe5d9 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -39,7 +39,7 @@
import android.text.format.DateUtils;
import android.util.Slog;
-import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
+import com.android.internal.infra.AbstractSinglePendingRequestRemoteService;
final class RemoteFillService
extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> {
@@ -77,7 +77,7 @@
try {
mService.onConnectedStateChanged(state);
} catch (Exception e) {
- Slog.w(mTag, "Exception calling onConnectedStateChanged(): " + e);
+ Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e);
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index a533640..7049744 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -117,26 +117,16 @@
* @param userId User id on which the backup operation is being requested.
* @param message A message to include in the exception if it is thrown.
*/
- private void enforceCallingPermissionOnUserId(int userId, String message) {
+ private void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
if (Binder.getCallingUserHandle().getIdentifier() != userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
}
}
- /**
- * Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
- * a background thread to keep the unlock time down.
- */
- public void unlockSystemUser() {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
- try {
- sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
- } catch (RemoteException e) {
- // can't happen; it's a local object
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
+ // ---------------------------------------------
+ // USER LIFECYCLE CALLBACKS
+ // ---------------------------------------------
/**
* Starts the backup service for user {@code userId} by creating a new instance of {@link
@@ -146,16 +136,31 @@
protected void startServiceForUser(int userId) {
UserBackupManagerService userBackupManagerService =
UserBackupManagerService.createAndInitializeService(
- mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+ userId, 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.
+ * UserBackupManagerService} with this service and setting enabled state.
*/
void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
mServiceUsers.put(userId, userBackupManagerService);
+
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+ try {
+ // TODO(b/121198604): Make enable file per-user and clean up indirection.
+ mTrampoline.setBackupEnabledForUser(userId, readBackupEnableState(userId));
+ } catch (RemoteException e) {
+ // Can't happen, it's a local object.
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Stops the backup service for user {@code userId} when the user is stopped. */
+ @VisibleForTesting
+ protected void stopServiceForUser(int userId) {
+ mServiceUsers.remove(userId);
}
SparseArray<UserBackupManagerService> getServiceUsers() {
@@ -170,9 +175,14 @@
* @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.
+ * @throws SecurityException if {@code userId} is different from the calling user id and the
+ * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
*/
@Nullable
- private UserBackupManagerService getServiceForUser(@UserIdInt int userId, String caller) {
+ @VisibleForTesting
+ UserBackupManagerService getServiceForUserIfCallerHasPermission(
+ @UserIdInt int userId, String caller) {
+ enforceCallingPermissionOnUserId(userId, caller);
UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
if (userBackupManagerService == null) {
Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
@@ -196,9 +206,9 @@
* backup for their app {@code packageName}. Only used for apps participating in key-value
* backup.
*/
- public void dataChanged(String packageName) {
+ public void dataChanged(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "dataChanged()");
+ getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
if (userBackupManagerService != null) {
userBackupManagerService.dataChanged(packageName);
@@ -209,9 +219,9 @@
* Callback: a requested backup agent has been instantiated. This should only be called from the
* {@link ActivityManager}.
*/
- public void agentConnected(String packageName, IBinder agentBinder) {
+ public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "agentConnected()");
+ getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
if (userBackupManagerService != null) {
userBackupManagerService.agentConnected(packageName, agentBinder);
@@ -222,9 +232,9 @@
* Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
* called from the {@link ActivityManager}.
*/
- public void agentDisconnected(String packageName) {
+ public void agentDisconnected(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "agentDisconnected()");
+ getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
if (userBackupManagerService != null) {
userBackupManagerService.agentDisconnected(packageName);
@@ -235,9 +245,9 @@
* Used by a currently-active backup agent to notify the service that it has completed its given
* outstanding asynchronous backup/restore operation.
*/
- public void opComplete(int token, long result) {
+ public void opComplete(@UserIdInt int userId, int token, long result) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "opComplete()");
+ getServiceForUserIfCallerHasPermission(userId, "opComplete()");
if (userBackupManagerService != null) {
userBackupManagerService.opComplete(token, result);
@@ -249,9 +259,10 @@
// ---------------------------------------------
/** Run an initialize operation for the given transports {@code transportNames}. */
- public void initializeTransports(String[] transportNames, IBackupObserver observer) {
+ public void initializeTransports(
+ @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "initializeTransports()");
+ getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
if (userBackupManagerService != null) {
userBackupManagerService.initializeTransports(transportNames, observer);
@@ -262,9 +273,9 @@
* Clear the given package {@code packageName}'s backup data from the transport {@code
* transportName}.
*/
- public void clearBackupData(String transportName, String packageName) {
+ public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "clearBackupData()");
+ getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
if (userBackupManagerService != null) {
userBackupManagerService.clearBackupData(transportName, packageName);
@@ -273,9 +284,9 @@
/** Return the name of the currently active transport. */
@Nullable
- public String getCurrentTransport() {
+ public String getCurrentTransport(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransport()");
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
return userBackupManagerService == null
? null
@@ -287,9 +298,9 @@
* null} if no transport selected or if the transport selected is not registered.
*/
@Nullable
- public ComponentName getCurrentTransportComponent() {
+ public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransportComponent()");
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
return userBackupManagerService == null
? null
@@ -298,9 +309,9 @@
/** Report all known, available backup transports by name. */
@Nullable
- public String[] listAllTransports() {
+ public String[] listAllTransports(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransports()");
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
return userBackupManagerService == null
? null
@@ -309,9 +320,9 @@
/** Report all known, available backup transports by {@link ComponentName}. */
@Nullable
- public ComponentName[] listAllTransportComponents() {
+ public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransportComponents()");
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
return userBackupManagerService == null
? null
@@ -321,12 +332,14 @@
/** Report all system whitelisted transports. */
@Nullable
public String[] getTransportWhitelist() {
- UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getTransportWhitelist()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getTransportWhitelist();
+ // No permission check, intentionally.
+ String[] whitelistedTransports = new String[mTransportWhitelist.size()];
+ int i = 0;
+ for (ComponentName component : mTransportWhitelist) {
+ whitelistedTransports[i] = component.flattenToShortString();
+ i++;
+ }
+ return whitelistedTransports;
}
/**
@@ -353,6 +366,7 @@
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
public void updateTransportAttributes(
+ @UserIdInt int userId,
ComponentName transportComponent,
String name,
@Nullable Intent configurationIntent,
@@ -360,7 +374,7 @@
@Nullable Intent dataManagementIntent,
String dataManagementLabel) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "updateTransportAttributes()");
+ getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
if (userBackupManagerService != null) {
userBackupManagerService.updateTransportAttributes(
@@ -381,9 +395,9 @@
*/
@Deprecated
@Nullable
- public String selectBackupTransport(String transportName) {
+ public String selectBackupTransport(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransport()");
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
return userBackupManagerService == null
? null
@@ -395,9 +409,11 @@
* with the result upon completion.
*/
public void selectBackupTransportAsync(
- ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+ @UserIdInt int userId,
+ ComponentName transportComponent,
+ ISelectBackupTransportCallback listener) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransportAsync()");
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
if (userBackupManagerService != null) {
userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
@@ -410,9 +426,9 @@
* returns {@code null}.
*/
@Nullable
- public Intent getConfigurationIntent(String transportName) {
+ public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getConfigurationIntent()");
+ getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
return userBackupManagerService == null
? null
@@ -429,9 +445,9 @@
* @return The current destination string or null if the transport is not registered.
*/
@Nullable
- public String getDestinationString(String transportName) {
+ public String getDestinationString(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getDestinationString()");
+ getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
return userBackupManagerService == null
? null
@@ -440,9 +456,9 @@
/** Supply the manage-data intent for the given transport. */
@Nullable
- public Intent getDataManagementIntent(String transportName) {
+ public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementIntent()");
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
return userBackupManagerService == null
? null
@@ -454,9 +470,9 @@
* transport.
*/
@Nullable
- public String getDataManagementLabel(String transportName) {
+ public String getDataManagementLabel(@UserIdInt int userId, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementLabel()");
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
return userBackupManagerService == null
? null
@@ -469,9 +485,8 @@
/** Enable/disable the backup service. This is user-configurable via backup settings. */
public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
- enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "setBackupEnabled()");
+ getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
if (userBackupManagerService != null) {
userBackupManagerService.setBackupEnabled(enable);
@@ -479,32 +494,21 @@
}
/** Enable/disable automatic restore of app data at install time. */
- public void setAutoRestore(boolean autoRestore) {
+ public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "setAutoRestore()");
+ getServiceForUserIfCallerHasPermission(userId, "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) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "setBackupProvisioned()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.setBackupProvisioned(provisioned);
- }
- }
-
/**
* Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
public boolean isBackupEnabled(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "isBackupEnabled()");
+ getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
}
@@ -514,9 +518,9 @@
// ---------------------------------------------
/** Checks if the given package {@code packageName} is eligible for backup. */
- public boolean isAppEligibleForBackup(String packageName) {
+ public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "isAppEligibleForBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
return userBackupManagerService != null
&& userBackupManagerService.isAppEligibleForBackup(packageName);
@@ -526,9 +530,9 @@
* Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
*/
@Nullable
- public String[] filterAppsEligibleForBackup(String[] packages) {
+ public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "filterAppsEligibleForBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
return userBackupManagerService == null
? null
@@ -540,9 +544,8 @@
* they have pending updates.
*/
public void backupNow(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "backupNow");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "backupNow()");
+ getServiceForUserIfCallerHasPermission(userId, "backupNow()");
if (userBackupManagerService != null) {
userBackupManagerService.backupNow();
@@ -559,9 +562,8 @@
IBackupObserver observer,
IBackupManagerMonitor monitor,
int flags) {
- enforceCallingPermissionOnUserId(userId, "requestBackup");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "requestBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
return userBackupManagerService == null
? BackupManager.ERROR_BACKUP_NOT_ALLOWED
@@ -570,9 +572,8 @@
/** Cancel all running backup operations. */
public void cancelBackups(@UserIdInt int userId) {
- enforceCallingPermissionOnUserId(userId, "cancelBackups");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "cancelBackups()");
+ getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
if (userBackupManagerService != null) {
userBackupManagerService.cancelBackups();
@@ -589,7 +590,7 @@
*/
public boolean beginFullBackup(FullBackupJob scheduledJob) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "beginFullBackup()");
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
return userBackupManagerService != null
&& userBackupManagerService.beginFullBackup(scheduledJob);
@@ -601,7 +602,7 @@
*/
public void endFullBackup() {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "endFullBackup()");
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.endFullBackup();
@@ -611,9 +612,9 @@
/**
* Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
*/
- public void fullTransportBackup(String[] packageNames) {
+ public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "fullTransportBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.fullTransportBackup(packageNames);
@@ -628,9 +629,9 @@
* Used to run a restore pass for an application that is being installed. This should only be
* called from the {@link PackageManager}.
*/
- public void restoreAtInstall(String packageName, int token) {
+ public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "restoreAtInstall()");
+ getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
if (userBackupManagerService != null) {
userBackupManagerService.restoreAtInstall(packageName, token);
@@ -642,9 +643,10 @@
* {@code transportName}.
*/
@Nullable
- public IRestoreSession beginRestoreSession(String packageName, String transportName) {
+ public IRestoreSession beginRestoreSession(
+ @UserIdInt int userId, String packageName, String transportName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "beginRestoreSession()");
+ getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
return userBackupManagerService == null
? null
@@ -655,9 +657,9 @@
* Get the restore-set token for the best-available restore set for this {@code packageName}:
* the active set if possible, else the ancestral one. Returns zero if none available.
*/
- public long getAvailableRestoreToken(String packageName) {
+ public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "getAvailableRestoreToken()");
+ getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
return userBackupManagerService == null
? 0
@@ -671,7 +673,8 @@
/** Sets the backup password used when running adb backup. */
public boolean setBackupPassword(String currentPassword, String newPassword) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "setBackupPassword()");
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "setBackupPassword()");
return userBackupManagerService != null
&& userBackupManagerService.setBackupPassword(currentPassword, newPassword);
@@ -680,7 +683,8 @@
/** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
public boolean hasBackupPassword() {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "hasBackupPassword()");
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "hasBackupPassword()");
return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
}
@@ -703,9 +707,8 @@
boolean doCompress,
boolean doKeyValue,
String[] packageNames) {
- enforceCallingPermissionOnUserId(userId, "adbBackup");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "adbBackup()");
+ getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
if (userBackupManagerService != null) {
userBackupManagerService.adbBackup(
@@ -728,9 +731,8 @@
* requires on-screen confirmation by the user.
*/
public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
- enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
UserBackupManagerService userBackupManagerService =
- getServiceForUser(userId, "adbRestore()");
+ getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
if (userBackupManagerService != null) {
userBackupManagerService.adbRestore(fd);
@@ -742,13 +744,14 @@
* to require a user-facing disclosure about the operation.
*/
public void acknowledgeAdbBackupOrRestore(
+ @UserIdInt int userId,
int token,
boolean allow,
String currentPassword,
String encryptionPassword,
IFullBackupRestoreObserver observer) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "acknowledgeAdbBackupOrRestore()");
+ getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
if (userBackupManagerService != null) {
userBackupManagerService.acknowledgeAdbBackupOrRestore(
@@ -763,7 +766,7 @@
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
UserBackupManagerService userBackupManagerService =
- getServiceForUser(UserHandle.USER_SYSTEM, "dump()");
+ getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
if (userBackupManagerService != null) {
userBackupManagerService.dump(fd, pw, args);
@@ -823,10 +826,14 @@
@Override
public void onUnlockUser(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
- sInstance.initializeServiceAndUnlockSystemUser();
- } else {
- sInstance.unlockUser(userId);
+ sInstance.initializeService();
}
+ sInstance.unlockUser(userId);
+ }
+
+ @Override
+ public void onStopUser(int userId) {
+ sInstance.stopUser(userId);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 59b72f9..79d4a2c 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -150,61 +150,38 @@
}
/**
- * Initialize {@link BackupManagerService} if the backup service is not disabled. Only the
- * system user can initialize the service.
- */
- /* package */ void initializeService(int userId) {
- if (mGlobalDisable) {
- Slog.i(TAG, "Backup service not supported");
- return;
- }
-
- if (userId != UserHandle.USER_SYSTEM) {
- Slog.i(TAG, "Cannot initialize backup service for non-system user: " + userId);
- return;
- }
-
- synchronized (mStateLock) {
- if (!mSuppressFile.exists()) {
- mService = createBackupManagerService();
- } else {
- Slog.i(TAG, "Backup service inactive");
- }
- }
- }
-
- /**
* Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
- * to initialize {@link BackupManagerService} and set backup state for the system user.
+ * to initialize {@link BackupManagerService}. Offloads work onto the handler thread {@link
+ * #mHandlerThread} to keep unlock time low.
*/
- void initializeServiceAndUnlockSystemUser() {
+ void initializeService() {
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();
+ if (mGlobalDisable) {
+ Slog.i(TAG, "Backup service not supported");
+ return;
}
+
+ synchronized (mStateLock) {
+ if (!mSuppressFile.exists()) {
+ mService = createBackupManagerService();
+ } else {
+ Slog.i(TAG, "Backup service inactive");
+ }
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
});
}
/**
- * Called from {@link BackupManagerService.Lifecycle} when a non-system user {@code userId} is
- * unlocked. Starts the backup service for this user if the service supports multi-user.
- * Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time low.
+ * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
+ * Starts the backup service for this user if it's the system user or if the service supports
+ * multi-user. Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time
+ * low.
*/
- // TODO(b/120212806): Consolidate service start for system and non-system users when system
- // user-only logic is removed.
void unlockUser(int userId) {
- if (!isMultiUserEnabled()) {
+ if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
return;
}
@@ -221,6 +198,26 @@
}
/**
+ * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
+ * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
+ */
+ void stopUser(int userId) {
+ if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
+ Slog.i(TAG, "Multi-user disabled, cannot stop service for user: " + userId);
+ return;
+ }
+
+ postToHandler(
+ () -> {
+ BackupManagerService service = mService;
+ if (service != null) {
+ Slog.i(TAG, "Stopping service for user: " + userId);
+ service.stopServiceForUser(userId);
+ }
+ });
+ }
+
+ /**
* Only privileged callers should be changing the backup state. This method only acts on {@link
* UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
* system user also deactivates backup in all users.
@@ -284,53 +281,81 @@
}
@Override
- public void dataChanged(String packageName) throws RemoteException {
+ public void dataChangedForUser(int userId, String packageName) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.dataChanged(packageName);
+ svc.dataChanged(userId, packageName);
}
}
@Override
- public void initializeTransports(String[] transportNames, IBackupObserver observer)
+ public void dataChanged(String packageName) throws RemoteException {
+ dataChangedForUser(binderGetCallingUserId(), packageName);
+ }
+
+ @Override
+ public void initializeTransportsForUser(
+ int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.initializeTransports(userId, transportNames, observer);
+ }
+ }
+
+ @Override
+ public void clearBackupDataForUser(int userId, String transportName, String packageName)
throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.initializeTransports(transportNames, observer);
+ svc.clearBackupData(userId, transportName, packageName);
}
}
@Override
public void clearBackupData(String transportName, String packageName)
throws RemoteException {
+ clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
+ }
+
+ @Override
+ public void agentConnectedForUser(int userId, String packageName, IBinder agent)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.clearBackupData(transportName, packageName);
+ svc.agentConnected(userId, packageName, agent);
}
}
@Override
public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+ agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
+ }
+
+ @Override
+ public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.agentConnected(packageName, agent);
+ svc.agentDisconnected(userId, packageName);
}
}
@Override
public void agentDisconnected(String packageName) throws RemoteException {
+ agentDisconnectedForUser(binderGetCallingUserId(), packageName);
+ }
+
+ @Override
+ public void restoreAtInstallForUser(int userId, String packageName, int token)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.agentDisconnected(packageName);
+ svc.restoreAtInstall(userId, packageName, token);
}
}
@Override
public void restoreAtInstall(String packageName, int token) throws RemoteException {
- BackupManagerService svc = mService;
- if (svc != null) {
- svc.restoreAtInstall(packageName, token);
- }
+ restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
}
@Override
@@ -348,19 +373,16 @@
}
@Override
- public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+ public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.setAutoRestore(doAutoRestore);
+ svc.setAutoRestore(userId, doAutoRestore);
}
}
@Override
- public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
- BackupManagerService svc = mService;
- if (svc != null) {
- svc.setBackupProvisioned(isProvisioned);
- }
+ public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+ setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
}
@Override
@@ -411,10 +433,11 @@
}
@Override
- public void fullTransportBackup(String[] packageNames) throws RemoteException {
+ public void fullTransportBackupForUser(int userId, String[] packageNames)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.fullTransportBackup(packageNames);
+ svc.fullTransportBackup(userId, packageNames);
}
}
@@ -427,20 +450,40 @@
}
@Override
- public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
- String encryptionPassword, IFullBackupRestoreObserver observer)
- throws RemoteException {
+ public void acknowledgeFullBackupOrRestoreForUser(
+ int userId,
+ int token,
+ boolean allow,
+ String curPassword,
+ String encryptionPassword,
+ IFullBackupRestoreObserver observer)
+ throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.acknowledgeAdbBackupOrRestore(token, allow,
+ svc.acknowledgeAdbBackupOrRestore(userId, token, allow,
curPassword, encryptionPassword, observer);
}
}
@Override
- public String getCurrentTransport() throws RemoteException {
+ public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
+ String encryptionPassword, IFullBackupRestoreObserver observer)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getCurrentTransport() : null;
+ acknowledgeFullBackupOrRestoreForUser(
+ binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
+ }
+
+
+ @Override
+ public String getCurrentTransportForUser(int userId) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getCurrentTransport(userId) : null;
+ }
+
+ @Override
+ public String getCurrentTransport() throws RemoteException {
+ return getCurrentTransportForUser(binderGetCallingUserId());
}
/**
@@ -449,21 +492,26 @@
*/
@Override
@Nullable
- public ComponentName getCurrentTransportComponent() {
+ public ComponentName getCurrentTransportComponentForUser(int userId) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getCurrentTransportComponent() : null;
+ return (svc != null) ? svc.getCurrentTransportComponent(userId) : null;
+ }
+
+ @Override
+ public String[] listAllTransportsForUser(int userId) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.listAllTransports(userId) : null;
}
@Override
public String[] listAllTransports() throws RemoteException {
- BackupManagerService svc = mService;
- return (svc != null) ? svc.listAllTransports() : null;
+ return listAllTransportsForUser(binderGetCallingUserId());
}
@Override
- public ComponentName[] listAllTransportComponents() throws RemoteException {
+ public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.listAllTransportComponents() : null;
+ return (svc != null) ? svc.listAllTransportComponents(userId) : null;
}
@Override
@@ -473,7 +521,8 @@
}
@Override
- public void updateTransportAttributes(
+ public void updateTransportAttributesForUser(
+ int userId,
ComponentName transportComponent,
String name,
@Nullable Intent configurationIntent,
@@ -483,6 +532,7 @@
BackupManagerService svc = mService;
if (svc != null) {
svc.updateTransportAttributes(
+ userId,
transportComponent,
name,
configurationIntent,
@@ -493,17 +543,23 @@
}
@Override
- public String selectBackupTransport(String transport) throws RemoteException {
+ public String selectBackupTransportForUser(int userId, String transport)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.selectBackupTransport(transport) : null;
+ return (svc != null) ? svc.selectBackupTransport(userId, transport) : null;
}
@Override
- public void selectBackupTransportAsync(ComponentName transport,
+ public String selectBackupTransport(String transport) throws RemoteException {
+ return selectBackupTransportForUser(binderGetCallingUserId(), transport);
+ }
+
+ @Override
+ public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
ISelectBackupTransportCallback listener) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.selectBackupTransportAsync(transport, listener);
+ svc.selectBackupTransportAsync(userId, transport, listener);
} else {
if (listener != null) {
try {
@@ -516,60 +572,86 @@
}
@Override
- public Intent getConfigurationIntent(String transport) throws RemoteException {
+ public Intent getConfigurationIntentForUser(int userId, String transport)
+ throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getConfigurationIntent(transport) : null;
+ return (svc != null) ? svc.getConfigurationIntent(userId, transport) : null;
+ }
+
+ @Override
+ public Intent getConfigurationIntent(String transport)
+ throws RemoteException {
+ return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
+ }
+
+ @Override
+ public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getDestinationString(userId, transport) : null;
}
@Override
public String getDestinationString(String transport) throws RemoteException {
- BackupManagerService svc = mService;
- return (svc != null) ? svc.getDestinationString(transport) : null;
+ return getDestinationStringForUser(binderGetCallingUserId(), transport);
}
@Override
- public Intent getDataManagementIntent(String transport) throws RemoteException {
- BackupManagerService svc = mService;
- return (svc != null) ? svc.getDataManagementIntent(transport) : null;
- }
-
- @Override
- public String getDataManagementLabel(String transport) throws RemoteException {
- BackupManagerService svc = mService;
- return (svc != null) ? svc.getDataManagementLabel(transport) : null;
- }
-
- @Override
- public IRestoreSession beginRestoreSession(String packageName, String transportID)
+ public Intent getDataManagementIntentForUser(int userId, String transport)
throws RemoteException {
BackupManagerService svc = mService;
- return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
+ return (svc != null) ? svc.getDataManagementIntent(userId, transport) : null;
+ }
+
+ @Override
+ public Intent getDataManagementIntent(String transport)
+ throws RemoteException {
+ return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
+ }
+
+ @Override
+ public String getDataManagementLabelForUser(int userId, String transport)
+ throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getDataManagementLabel(userId, transport) : null;
+ }
+
+ @Override
+ public String getDataManagementLabel(String transport)
+ throws RemoteException {
+ return getDataManagementLabelForUser(binderGetCallingUserId(), transport);
+ }
+
+ @Override
+ public IRestoreSession beginRestoreSessionForUser(
+ int userId, String packageName, String transportID) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.beginRestoreSession(userId, packageName, transportID) : null;
}
@Override
public void opComplete(int token, long result) throws RemoteException {
BackupManagerService svc = mService;
if (svc != null) {
- svc.opComplete(token, result);
+ svc.opComplete(binderGetCallingUserId(), token, result);
}
}
@Override
- public long getAvailableRestoreToken(String packageName) {
+ public long getAvailableRestoreTokenForUser(int userId, String packageName) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0;
+ return (svc != null) ? svc.getAvailableRestoreToken(userId, packageName) : 0;
}
@Override
- public boolean isAppEligibleForBackup(String packageName) {
+ public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false;
+ return (svc != null) ? svc.isAppEligibleForBackup(userId, packageName) : false;
}
@Override
- public String[] filterAppsEligibleForBackup(String[] packages) {
+ public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
BackupManagerService svc = mService;
- return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null;
+ return (svc != null) ? svc.filterAppsEligibleForBackup(userId, packages) : null;
}
@Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 796ef40..2e41443 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -251,6 +251,7 @@
private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+ private final @UserIdInt int mUserId;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final TransportManager mTransportManager;
@@ -372,10 +373,11 @@
* Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
* includes setting up the directories where we keep our bookkeeping and transport management.
*
- * @see #createAndInitializeService(Context, Trampoline, HandlerThread, File, File,
+ * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
* TransportManager)
*/
static UserBackupManagerService createAndInitializeService(
+ @UserIdInt int userId,
Context context,
Trampoline trampoline,
HandlerThread backupThread,
@@ -399,12 +401,13 @@
File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
return createAndInitializeService(
- context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+ userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
}
/**
* Creates an instance of {@link UserBackupManagerService}.
*
+ * @param userId The user which this service is for.
* @param context The system server context.
* @param trampoline A reference to the proxy to {@link BackupManagerService}.
* @param backupThread The thread running backup/restore operations for the user.
@@ -415,6 +418,7 @@
*/
@VisibleForTesting
public static UserBackupManagerService createAndInitializeService(
+ @UserIdInt int userId,
Context context,
Trampoline trampoline,
HandlerThread backupThread,
@@ -422,16 +426,18 @@
File dataDir,
TransportManager transportManager) {
return new UserBackupManagerService(
- context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+ userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
}
private UserBackupManagerService(
+ @UserIdInt int userId,
Context context,
Trampoline parent,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
+ mUserId = userId;
mContext = checkNotNull(context, "context cannot be null");
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -2805,15 +2811,6 @@
}
}
- /** Mark the backup service as having been provisioned. */
- public void setBackupProvisioned(boolean available) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupProvisioned");
- /*
- * This is now a no-op; provisioning is simply the device's own setup state.
- */
- }
-
/** Report whether the backup mechanism is currently enabled. */
public boolean isBackupEnabled() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -2863,19 +2860,6 @@
return mTransportManager.getRegisteredTransportComponents();
}
- /** Report all system whitelisted transports. */
- public String[] getTransportWhitelist() {
- // No permission check, intentionally.
- Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
- String[] whitelistedTransports = new String[whitelistedComponents.size()];
- int i = 0;
- for (ComponentName component : whitelistedComponents) {
- whitelistedTransports[i] = component.flattenToShortString();
- i++;
- }
- return whitelistedTransports;
- }
-
/**
* Update the attributes of the transport identified by {@code transportComponent}. If the
* specified transport has not been bound at least once (for registration), this call will be
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index e8820ae..4b24ef9 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -20,7 +20,6 @@
import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
@@ -34,7 +33,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.IContentCaptureManager;
import com.android.internal.annotations.GuardedBy;
@@ -43,6 +41,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -68,14 +67,16 @@
private final LocalService mLocalService = new LocalService();
- public ContentCaptureManagerService(Context context) {
- super(context, UserManager.DISALLOW_CONTENT_CAPTURE);
+ public ContentCaptureManagerService(@NonNull Context context) {
+ super(context, new FrameworkResourcesServiceNameResolver(context,
+ com.android.internal.R.string.config_defaultContentCaptureService),
+ UserManager.DISALLOW_CONTENT_CAPTURE);
}
@Override // from AbstractMasterSystemService
protected ContentCapturePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
- return new ContentCapturePerUserService(this, mLock, resolvedUserId);
+ return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId);
}
@Override // from SystemService
@@ -164,8 +165,7 @@
@Override
public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
- @NonNull ComponentName componentName, @NonNull String sessionId,
- @Nullable ContentCaptureContext clientContext, int flags,
+ @NonNull ComponentName componentName, @NonNull String sessionId, int flags,
@NonNull IResultReceiver result) {
Preconditions.checkNotNull(activityToken);
Preconditions.checkNotNull(componentName);
@@ -175,14 +175,13 @@
// so we don't pass it on startSession (same for Autofill)
final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false);
- // TODO(b/111276913): get from AM as well
+ // TODO(b/121260224): get from AM as well
final int displayId = 0;
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, componentName, taskId, displayId,
- sessionId, Binder.getCallingUid(), clientContext, flags,
- mAllowInstantService, result);
+ sessionId, Binder.getCallingUid(), flags, result);
}
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index f21b0d8..c467935 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -24,7 +24,6 @@
import android.Manifest;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.assist.AssistContent;
@@ -36,16 +35,16 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.SnapshotData;
import android.util.ArrayMap;
import android.util.Slog;
-import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureSession;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
+import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
import com.android.server.infra.AbstractPerUserSystemService;
-import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -55,7 +54,8 @@
*/
final class ContentCapturePerUserService
extends
- AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService> {
+ AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService>
+ implements ContentCaptureServiceCallbacks {
private static final String TAG = ContentCaptureManagerService.class.getSimpleName();
@@ -63,16 +63,52 @@
private final ArrayMap<String, ContentCaptureServerSession> mSessions =
new ArrayMap<>();
+ /**
+ * Reference to the remote service.
+ *
+ * <p>It's set in the constructor, but it's also updated when the service's updated in the
+ * master's cache (for example, because a temporary service was set).
+ */
+ @GuardedBy("mLock")
+ private RemoteContentCaptureService mRemoteService;
+
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
- protected ContentCapturePerUserService(
- ContentCaptureManagerService master, Object lock, @UserIdInt int userId) {
- super(master, new FrameworkResourcesServiceNameResolver(master.getContext(), userId, lock,
- com.android.internal.R.string.config_defaultContentCaptureService), lock, userId);
+ ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
+ @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
+ super(master, lock, userId);
+
+ updateRemoteServiceLocked(disabled);
+ }
+
+ /**
+ * Updates the reference to the remote service.
+ */
+ private void updateRemoteServiceLocked(boolean disabled) {
+ if (mRemoteService != null) {
+ if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+ mRemoteService.destroy();
+ mRemoteService = null;
+ }
+
+ // Updates the component name
+ final ComponentName serviceComponentName = updateServiceInfoLocked();
+
+ if (serviceComponentName == null) {
+ Slog.w(TAG, "updateRemoteService(): no service componennt name");
+ return;
+ }
+
+ if (!disabled) {
+ mRemoteService = new RemoteContentCaptureService(
+ mMaster.getContext(),
+ ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, mUserId, this,
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ }
}
@Override // from PerUserSystemService
- protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
int flags = PackageManager.GET_META_DATA;
@@ -107,15 +143,25 @@
@GuardedBy("mLock")
protected boolean updateLocked(boolean disabled) {
destroyLocked();
- return super.updateLocked(disabled);
+ final boolean disabledStateChanged = super.updateLocked(disabled);
+ updateRemoteServiceLocked(disabled);
+ return disabledStateChanged;
}
- // TODO(b/111276913): log metrics
+ @Override // from ContentCaptureServiceCallbacks
+ public void onServiceDied(@NonNull RemoteContentCaptureService service) {
+ if (mMaster.debug) Slog.d(TAG, "remote service died: " + service);
+ synchronized (mLock) {
+ removeSelfFromCacheLocked();
+ }
+ }
+
+ // TODO(b/119613670): log metrics
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
@NonNull ComponentName componentName, int taskId, int displayId,
- @NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext,
- int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver clientReceiver) {
+ @NonNull String sessionId, int uid, int flags,
+ @NonNull IResultReceiver clientReceiver) {
if (!isEnabledLocked()) {
setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
return;
@@ -140,10 +186,23 @@
return;
}
- final ContentCaptureServerSession newSession = new ContentCaptureServerSession(getContext(),
- mUserId, mLock, activityToken, this, serviceComponentName, componentName, taskId,
- displayId, sessionId, uid, clientContext, flags, bindInstantServiceAllowed,
- mMaster.verbose);
+ if (mRemoteService == null) {
+ updateRemoteServiceLocked(/* disabled= */ false); // already checked for isEnabled
+ }
+
+ if (mRemoteService == null) {
+ // TODO(b/119613670): log metrics
+ Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+ + ": ignoring because service is not set");
+ // TODO(b/111276913): use a new disabled state?
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED,
+ /* binder=*/ null);
+ return;
+ }
+
+ final ContentCaptureServerSession newSession = new ContentCaptureServerSession(
+ activityToken, this, mRemoteService, componentName, taskId,
+ displayId, sessionId, uid, flags);
if (mMaster.verbose) {
Slog.v(TAG, "startSession(): new session for "
+ ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
@@ -152,7 +211,7 @@
newSession.notifySessionStartedLocked(clientReceiver);
}
- // TODO(b/111276913): log metrics
+ // TODO(b/119613670): log metrics
@GuardedBy("mLock")
public void finishSessionLocked(@NonNull String sessionId) {
if (!isEnabledLocked()) {
@@ -243,12 +302,18 @@
@Override
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
+
+ final String prefix2 = prefix + " ";
+ if (mRemoteService != null) {
+ pw.print(prefix); pw.println("remote service:");
+ mRemoteService.dump(prefix2, pw);
+ }
+
if (mSessions.isEmpty()) {
pw.print(prefix); pw.println("no sessions");
} else {
final int size = mSessions.size();
pw.print(prefix); pw.print("number sessions: "); pw.println(size);
- final String prefix2 = prefix + " ";
for (int i = 0; i < size; i++) {
pw.print(prefix); pw.print("session@"); pw.println(i);
final ContentCaptureServerSession session = mSessions.valueAt(i);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index ba98b95..ebe0083 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -16,9 +16,7 @@
package com.android.server.contentcapture;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.ComponentName;
-import android.content.Context;
import android.os.IBinder;
import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.SnapshotData;
@@ -29,15 +27,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
-import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
import java.io.PrintWriter;
-final class ContentCaptureServerSession implements ContentCaptureServiceCallbacks {
+final class ContentCaptureServerSession {
private static final String TAG = ContentCaptureServerSession.class.getSimpleName();
- private final Object mLock;
final IBinder mActivityToken;
private final ContentCapturePerUserService mService;
private final RemoteContentCaptureService mRemoteService;
@@ -53,22 +49,18 @@
*/
private final int mUid;
- ContentCaptureServerSession(@NonNull Context context, int userId, @NonNull Object lock,
- @NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
- @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
- int taskId, int displayId, @NonNull String sessionId, int uid,
- @Nullable ContentCaptureContext clientContext, int flags,
- boolean bindInstantServiceAllowed, boolean verbose) {
- mLock = lock;
+ ContentCaptureServerSession(@NonNull IBinder activityToken,
+ @NonNull ContentCapturePerUserService service,
+ @NonNull RemoteContentCaptureService remoteService,
+ @NonNull ComponentName appComponentName,
+ int taskId, int displayId, @NonNull String sessionId, int uid, int flags) {
mActivityToken = activityToken;
mService = service;
mId = Preconditions.checkNotNull(sessionId);
mUid = uid;
- mRemoteService = new RemoteContentCaptureService(context,
- ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
- bindInstantServiceAllowed, verbose);
- mContentCaptureContext = new ContentCaptureContext(clientContext, appComponentName, taskId,
- displayId, flags);
+ mRemoteService = remoteService;
+ mContentCaptureContext = new ContentCaptureContext(/* clientContext= */ null,
+ appComponentName, taskId, displayId, flags);
}
/**
@@ -128,17 +120,6 @@
}
}
- @Override // from RemoteContentCaptureServiceCallbacks
- public void onServiceDied(@NonNull RemoteContentCaptureService service) {
- // TODO(b/111276913): implement (remove session from PerUserSession?)
- if (mService.isDebug()) {
- Slog.d(TAG, "onServiceDied() for " + mId);
- }
- synchronized (mLock) {
- removeSelfLocked(/* notifyRemoteService= */ true);
- }
- }
-
@GuardedBy("mLock")
public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println();
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index b9b1943..82416286 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -23,17 +23,16 @@
import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.SnapshotData;
import android.text.format.DateUtils;
+import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.os.IResultReceiver;
-import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
final class RemoteContentCaptureService
extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
IContentCaptureService> {
- // TODO(b/117779333): changed it so it's permanentely bound
- private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
RemoteContentCaptureService(Context context, String serviceInterface,
@@ -42,26 +41,40 @@
boolean verbose) {
super(context, serviceInterface, componentName, userId, callbacks,
bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
+
+ // Bind right away, which will trigger a onConnected() on service's
+ scheduleBind();
}
- @Override // from RemoteService
+ @Override // from AbstractRemoteService
protected IContentCaptureService getServiceInterface(@NonNull IBinder service) {
return IContentCaptureService.Stub.asInterface(service);
}
- // TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
- @Override // from RemoteService
+ @Override // from AbstractRemoteService
protected long getTimeoutIdleBindMillis() {
// TODO(b/111276913): read from Settings so it can be changed in the field
- return TIMEOUT_IDLE_BIND_MILLIS;
+ return PERMANENT_BOUND_TIMEOUT_MS;
}
- @Override // from RemoteService
+ @Override // from AbstractRemoteService
protected long getRemoteRequestMillis() {
// TODO(b/111276913): read from Settings so it can be changed in the field
return TIMEOUT_REMOTE_REQUEST_MILLIS;
}
+ @Override // from RemoteService
+ protected void handleOnConnectedStateChanged(boolean state) {
+ if (state && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) {
+ scheduleUnbind();
+ }
+ try {
+ mService.onConnectedStateChanged(state);
+ } catch (Exception e) {
+ Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e);
+ }
+ }
+
/**
* Called by {@link ContentCaptureServerSession} to generate a call to the
* {@link RemoteContentCaptureService} to indicate the session was created.
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index f0ec69f..3cdf09e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -65,7 +65,6 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -1646,29 +1645,40 @@
}
@Override
+ public int checkOperationRaw(int code, int uid, String packageName) {
+ return checkOperationInternal(code, uid, packageName, true /*raw*/);
+ }
+
+ @Override
public int checkOperation(int code, int uid, String packageName) {
+ return checkOperationInternal(code, uid, packageName, false /*raw*/);
+ }
+
+ private int checkOperationInternal(int code, int uid, String packageName, boolean raw) {
final CheckOpsDelegate delegate;
synchronized (this) {
delegate = mCheckOpsDelegate;
}
if (delegate == null) {
- return checkOperationImpl(code, uid, packageName);
+ return checkOperationImpl(code, uid, packageName, raw);
}
- return delegate.checkOperation(code, uid, packageName,
+ return delegate.checkOperation(code, uid, packageName, raw,
AppOpsService.this::checkOperationImpl);
}
- private int checkOperationImpl(int code, int uid, String packageName) {
+ private int checkOperationImpl(int code, int uid, String packageName,
+ boolean raw) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
- return checkOperationUnchecked(code, uid, resolvedPackageName);
+ return checkOperationUnchecked(code, uid, resolvedPackageName, raw);
}
- private int checkOperationUnchecked(int code, int uid, String packageName) {
+ private int checkOperationUnchecked(int code, int uid, String packageName,
+ boolean raw) {
synchronized (this) {
if (isOpRestrictedLocked(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
@@ -1677,7 +1687,8 @@
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null && uidState.opModes != null
&& uidState.opModes.indexOfKey(code) >= 0) {
- return uidState.evalMode(uidState.opModes.get(code));
+ final int rawMode = uidState.opModes.get(code);
+ return raw ? rawMode : uidState.evalMode(rawMode);
}
Op op = getOpLocked(code, uid, packageName, false, true, false);
if (op == null) {
@@ -4085,10 +4096,15 @@
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
- try {
- packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
- } catch (RemoteException e) {
- /* ignore - local call */
+
+ // Very early during boot the package manager is not yet or not yet fully started. At this
+ // time there are no packages yet.
+ if (AppGlobals.getPackageManager() != null) {
+ try {
+ packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ }
}
if (packageNames == null) {
return EmptyArray.STRING;
@@ -4258,9 +4274,8 @@
}
@Override
- public void setMode(int code, int uid, @NonNull String packageName, int mode,
- boolean isPrivileged) {
- AppOpsService.this.setMode(code, uid, packageName, mode, false, isPrivileged);
+ public void setUidMode(int code, int uid, int mode) {
+ AppOpsService.this.setUidMode(code, uid, mode);
}
}
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 08cb7a2..121a830 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -87,6 +87,10 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
+import com.android.server.deviceidle.ConstraintController;
+import com.android.server.deviceidle.DeviceIdleConstraintTracker;
+import com.android.server.deviceidle.IDeviceIdleConstraint;
+import com.android.server.deviceidle.TvConstraintController;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -104,6 +108,7 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.stream.Collectors;
/**
* Keeps track of device idleness and drives low power mode based on that.
@@ -296,6 +301,17 @@
private Location mLastGpsLocation;
// Current locked state of the screen
private boolean mScreenLocked;
+ private int mNumBlockingConstraints = 0;
+
+ /**
+ * Constraints are the "handbrakes" that stop the device from moving into a lower state until
+ * every one is released at the same time.
+ *
+ * @see #registerDeviceIdleConstraintInternal(IDeviceIdleConstraint, String, int)
+ */
+ private final ArrayMap<IDeviceIdleConstraint, DeviceIdleConstraintTracker>
+ mConstraints = new ArrayMap<>();
+ private ConstraintController mConstraintController;
/** Device is currently active. */
@VisibleForTesting
@@ -703,8 +719,7 @@
* global Settings. Any access to this class or its fields should be done while
* holding the DeviceIdleController lock.
*/
- @VisibleForTesting
- final class Constants extends ContentObserver {
+ public final class Constants extends ContentObserver {
// Key names stored in the settings value.
private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
= "light_after_inactive_to";
@@ -1031,9 +1046,9 @@
INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
SENSING_TIMEOUT = mParser.getDurationMillis(KEY_SENSING_TIMEOUT,
- !DEBUG ? 4 * 60 * 1000L : 60 * 1000L);
+ !COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L);
LOCATING_TIMEOUT = mParser.getDurationMillis(KEY_LOCATING_TIMEOUT,
- !DEBUG ? 30 * 1000L : 15 * 1000L);
+ !COMPRESS_TIME ? 30 * 1000L : 15 * 1000L);
LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
MOTION_INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_MOTION_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
@@ -1228,6 +1243,7 @@
private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
private static final int MSG_FINISH_IDLE_OP = 8;
private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
+ private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
final class MyHandler extends Handler {
MyHandler(Looper looper) {
@@ -1348,6 +1364,15 @@
final boolean added = (msg.arg2 == 1);
mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added);
} break;
+ case MSG_SEND_CONSTRAINT_MONITORING: {
+ final IDeviceIdleConstraint constraint = (IDeviceIdleConstraint) msg.obj;
+ final boolean monitoring = (msg.arg1 == 1);
+ if (monitoring) {
+ constraint.startMonitoring();
+ } else {
+ constraint.stopMonitoring();
+ }
+ } break;
}
}
}
@@ -1512,6 +1537,25 @@
}
public class LocalService {
+ public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) {
+ synchronized (DeviceIdleController.this) {
+ onConstraintStateChangedLocked(constraint, active);
+ }
+ }
+
+ public void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name,
+ @IDeviceIdleConstraint.MinimumState int minState) {
+ registerDeviceIdleConstraintInternal(constraint, name, minState);
+ }
+
+ public void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint) {
+ unregisterDeviceIdleConstraintInternal(constraint);
+ }
+
+ public void exitIdle(String reason) {
+ exitIdleInternal(reason);
+ }
+
// duration in milliseconds
public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
long duration, int userId, boolean sync, String reason) {
@@ -1562,6 +1606,7 @@
static class Injector {
private final Context mContext;
private ConnectivityService mConnectivityService;
+ private Constants mConstants;
private LocationManager mLocationManager;
Injector(Context ctx) {
@@ -1591,7 +1636,10 @@
Constants getConstants(DeviceIdleController controller, Handler handler,
ContentResolver resolver) {
- return controller.new Constants(handler, resolver);
+ if (mConstants == null) {
+ mConstants = controller.new Constants(handler, resolver);
+ }
+ return mConstants;
}
LocationManager getLocationManager() {
@@ -1608,6 +1656,23 @@
PowerManager getPowerManager() {
return mContext.getSystemService(PowerManager.class);
}
+
+ SensorManager getSensorManager() {
+ return mContext.getSystemService(SensorManager.class);
+ }
+
+ ConstraintController getConstraintController(Handler handler, LocalService localService) {
+ if (mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
+ return new TvConstraintController(mContext, handler);
+ }
+ return null;
+ }
+
+ boolean useMotionSensor() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
+ }
}
private final Injector mInjector;
@@ -1632,9 +1697,7 @@
mHandler = mInjector.getHandler(this);
mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
-
- mUseMotionSensor = context.getResources().getBoolean(
- com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
+ mUseMotionSensor = mInjector.useMotionSensor();
}
public DeviceIdleController(Context context) {
@@ -1734,7 +1797,7 @@
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
- mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
+ mSensorManager = mInjector.getSensorManager();
if (mUseMotionSensor) {
int sigMotionSensorId = getContext().getResources().getInteger(
@@ -1763,6 +1826,12 @@
.setNumUpdates(1);
}
+ mConstraintController = mInjector.getConstraintController(
+ mHandler, getLocalService(LocalService.class));
+ if (mConstraintController != null) {
+ mConstraintController.start();
+ }
+
float angleThreshold = getContext().getResources().getInteger(
com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this,
@@ -1818,6 +1887,99 @@
}
}
+ @VisibleForTesting
+ boolean hasMotionSensor() {
+ return mUseMotionSensor && mMotionSensor != null;
+ }
+
+ private void registerDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint,
+ final String name, final int type) {
+ final int minState;
+ switch (type) {
+ case IDeviceIdleConstraint.ACTIVE:
+ minState = STATE_ACTIVE;
+ break;
+ case IDeviceIdleConstraint.SENSING_OR_ABOVE:
+ minState = STATE_SENSING;
+ break;
+ default:
+ Slog.wtf(TAG, "Registering device-idle constraint with invalid type: " + type);
+ return;
+ }
+ synchronized (this) {
+ if (mConstraints.containsKey(constraint)) {
+ Slog.e(TAG, "Re-registering device-idle constraint: " + constraint + ".");
+ return;
+ }
+ DeviceIdleConstraintTracker tracker = new DeviceIdleConstraintTracker(name, minState);
+ mConstraints.put(constraint, tracker);
+ updateActiveConstraintsLocked();
+ }
+ }
+
+ private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) {
+ synchronized (this) {
+ // Artifically force the constraint to inactive to unblock anything waiting for it.
+ onConstraintStateChangedLocked(constraint, /* active= */ false);
+
+ // Let the constraint know that we are not listening to it any more.
+ setConstraintMonitoringLocked(constraint, /* monitoring= */ false);
+ mConstraints.remove(constraint);
+ }
+ }
+
+ @GuardedBy("this")
+ private void onConstraintStateChangedLocked(IDeviceIdleConstraint constraint, boolean active) {
+ DeviceIdleConstraintTracker tracker = mConstraints.get(constraint);
+ if (tracker == null) {
+ Slog.e(TAG, "device-idle constraint " + constraint + " has not been registered.");
+ return;
+ }
+ if (active != tracker.active && tracker.monitoring) {
+ tracker.active = active;
+ mNumBlockingConstraints += (tracker.active ? +1 : -1);
+ if (mNumBlockingConstraints == 0) {
+ if (mState == STATE_ACTIVE) {
+ becomeInactiveIfAppropriateLocked();
+ } else if (mNextAlarmTime == 0 || mNextAlarmTime < SystemClock.elapsedRealtime()) {
+ stepIdleStateLocked("s:" + tracker.name);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("this")
+ private void setConstraintMonitoringLocked(IDeviceIdleConstraint constraint, boolean monitor) {
+ DeviceIdleConstraintTracker tracker = mConstraints.get(constraint);
+ if (tracker.monitoring != monitor) {
+ tracker.monitoring = monitor;
+ updateActiveConstraintsLocked();
+ // We send the callback on a separate thread instead of just relying on oneway as
+ // the client could be in the system server with us and cause re-entry problems.
+ mHandler.obtainMessage(MSG_SEND_CONSTRAINT_MONITORING,
+ /* monitoring= */ monitor ? 1 : 0,
+ /* <not used>= */ -1,
+ /* constraint= */ constraint).sendToTarget();
+ }
+ }
+
+ @GuardedBy("this")
+ private void updateActiveConstraintsLocked() {
+ mNumBlockingConstraints = 0;
+ for (int i = 0; i < mConstraints.size(); i++) {
+ final IDeviceIdleConstraint constraint = mConstraints.keyAt(i);
+ final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i);
+ final boolean monitoring = (tracker.minState == mState);
+ if (monitoring != tracker.monitoring) {
+ setConstraintMonitoringLocked(constraint, monitoring);
+ tracker.active = monitoring;
+ }
+ if (tracker.monitoring && tracker.active) {
+ mNumBlockingConstraints++;
+ }
+ }
+ }
+
public boolean addPowerSaveWhitelistAppInternal(String name) {
synchronized (this) {
try {
@@ -2448,6 +2610,7 @@
cancelLocatingLocked();
stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
+ updateActiveConstraintsLocked();
}
private void resetLightIdleManagementLocked() {
@@ -2583,40 +2746,50 @@
return;
}
+ if (mNumBlockingConstraints != 0 && !mForceIdle) {
+ // We have some constraints from other parts of the system server preventing
+ // us from moving to the next state.
+ if (DEBUG) {
+ Slog.i(TAG, "Cannot step idle state. Blocked by: " + mConstraints.values().stream()
+ .filter(x -> x.active)
+ .map(x -> x.name)
+ .collect(Collectors.joining(",")));
+ }
+ return;
+ }
+
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
// for motion and sleep some more while doing so.
startMonitoringMotionLocked();
scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
- mState = STATE_IDLE_PENDING;
- if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");
- EventLogTags.writeDeviceIdle(mState, reason);
+ moveToStateLocked(STATE_IDLE_PENDING, reason);
break;
case STATE_IDLE_PENDING:
- mState = STATE_SENSING;
- if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
- EventLogTags.writeDeviceIdle(mState, reason);
+ moveToStateLocked(STATE_SENSING, reason);
cancelLocatingLocked();
mLocated = false;
mLastGenericLocation = null;
mLastGpsLocation = null;
+ updateActiveConstraintsLocked();
- // If we have an accelerometer, wait to find out whether we are moving.
+ // Wait for open constraints and an accelerometer reading before moving on.
if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {
scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
mNotMoving = false;
mAnyMotionDetector.checkForAnyMotion();
break;
+ } else if (mNumBlockingConstraints != 0) {
+ cancelAlarmLocked();
+ break;
}
mNotMoving = true;
// Otherwise, fall through and check this off the list of requirements.
case STATE_SENSING:
cancelSensingTimeoutAlarmLocked();
- mState = STATE_LOCATING;
- if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
- EventLogTags.writeDeviceIdle(mState, reason);
+ moveToStateLocked(STATE_LOCATING, reason);
scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
LocationManager locationManager = mInjector.getLocationManager();
if (locationManager != null
@@ -2665,12 +2838,11 @@
if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
mNextIdleDelay = mConstants.IDLE_TIMEOUT;
}
- mState = STATE_IDLE;
+ moveToStateLocked(STATE_IDLE, reason);
if (mLightState != LIGHT_STATE_OVERRIDE) {
mLightState = LIGHT_STATE_OVERRIDE;
cancelLightAlarmLocked();
}
- EventLogTags.writeDeviceIdle(mState, reason);
addEvent(EVENT_DEEP_IDLE, null);
mGoingIdleWakeLock.acquire();
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
@@ -2688,14 +2860,24 @@
if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
}
- mState = STATE_IDLE_MAINTENANCE;
- EventLogTags.writeDeviceIdle(mState, reason);
+ moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
addEvent(EVENT_DEEP_MAINTENANCE, null);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
break;
}
}
+ private void moveToStateLocked(int state, String reason) {
+ final int oldState = mState;
+ mState = state;
+ if (DEBUG) {
+ Slog.d(TAG, String.format("Moved from STATE_%s to STATE_%s.",
+ stateToString(oldState), stateToString(mState)));
+ }
+ EventLogTags.writeDeviceIdle(mState, reason);
+ updateActiveConstraintsLocked();
+ }
+
void incActiveIdleOps() {
synchronized (this) {
mActiveIdleOpCount++;
@@ -2818,6 +3000,7 @@
mMaintenanceStartTime = 0;
EventLogTags.writeDeviceIdle(mState, type);
becomeInactive = true;
+ updateActiveConstraintsLocked();
}
if (mLightState == LIGHT_STATE_OVERRIDE) {
// We went out of light idle mode because we had started deep idle mode... let's
@@ -3794,8 +3977,22 @@
pw.print(" mScreenLocked="); pw.println(mScreenLocked);
pw.print(" mNetworkConnected="); pw.println(mNetworkConnected);
pw.print(" mCharging="); pw.println(mCharging);
- pw.print(" mMotionActive="); pw.println(mMotionListener.active);
+ if (mConstraints.size() != 0) {
+ pw.println(" mConstraints={");
+ for (int i = 0; i < mConstraints.size(); i++) {
+ final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i);
+ pw.print(" \""); pw.print(tracker.name); pw.print("\"=");
+ if (tracker.minState == mState) {
+ pw.println(tracker.active);
+ } else {
+ pw.print("ignored <mMinState="); pw.print(stateToString(tracker.minState));
+ pw.println(">");
+ }
+ }
+ pw.println(" }");
+ }
if (mUseMotionSensor) {
+ pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mNotMoving="); pw.println(mNotMoving);
}
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index d33b617..2346cfc 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -47,6 +47,7 @@
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
+import android.location.GnssMeasurementCorrections;
import android.location.IBatchedLocationCallback;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
@@ -2594,6 +2595,30 @@
}
@Override
+ public void injectGnssMeasurementCorrections(
+ GnssMeasurementCorrections measurementCorrections, String packageName) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to inject GNSS measurement corrections.");
+ if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
+ mGnssMeasurementsProvider.injectGnssMeasurementCorrections(measurementCorrections);
+ } else {
+ Slog.e(TAG, "Can not inject GNSS corrections due to no permission.");
+ }
+ }
+
+ @Override
+ public int getGnssCapabilities(String packageName) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to obrain GNSS chipset capabilities.");
+ if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
+ return -1;
+ }
+ return mGnssMeasurementsProvider.getGnssCapabilities();
+ }
+
+ @Override
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
if (mGnssMeasurementsProvider == null) {
return;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7adcaba..2a80644 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -129,6 +129,7 @@
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.AppFuseMount;
import com.android.internal.os.BackgroundThread;
@@ -1709,6 +1710,10 @@
ServiceManager.getService("package"));
mIAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
+ try {
+ mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
+ } catch (RemoteException e) {
+ }
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
}
@@ -3240,6 +3245,15 @@
}
}
+ private IAppOpsCallback.Stub mAppOpsCallback = new IAppOpsCallback.Stub() {
+ @Override
+ public void opChanged(int op, int uid, String packageName) throws RemoteException {
+ if (!ENABLE_ISOLATED_STORAGE) return;
+
+ remountUidExternalStorage(uid, getMountMode(uid, packageName));
+ }
+ };
+
private static final Pattern PATTERN_TRANSLATE = Pattern.compile(
"(?i)^(/storage/[^/]+/(?:[0-9]+/)?)(.*)");
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fe632e5..a94fa12 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2023,7 +2023,7 @@
if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
name.getPackageName(), sInfo.applicationInfo.uid)) {
String msg = "association not allowed between packages "
- + callingPackage + " and " + r.packageName;
+ + callingPackage + " and " + name.getPackageName();
Slog.w(TAG, "Service lookup failed: " + msg);
return new ServiceLookupResult(null, msg);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d114397..1a5dd90 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -646,6 +647,11 @@
final ProcessStatsService mProcessStats;
/**
+ * Service for compacting background apps.
+ */
+ final AppCompactor mAppCompact;
+
+ /**
* Non-persistent appId whitelist for background restrictions
*/
int[] mBackgroundAppIdWhitelist = new int[] {
@@ -795,11 +801,6 @@
*/
final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
- /**
- * Processes to compact.
- */
- final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>();
-
private boolean mBinderTransactionTrackingEnabled = false;
/**
@@ -1457,7 +1458,6 @@
final Handler mUiHandler;
final ServiceThread mProcStartHandlerThread;
final Handler mProcStartHandler;
- final ServiceThread mCompactionThread;
final ActivityManagerConstants mConstants;
@@ -1795,11 +1795,6 @@
}
};
- static final int COMPACT_PROCESS_SOME = 1;
- static final int COMPACT_PROCESS_FULL = 2;
- static final int COMPACT_PROCESS_MSG = 1;
- final Handler mCompactionHandler;
-
static final int COLLECT_PSS_BG_MSG = 1;
final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
@@ -2235,8 +2230,7 @@
? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
- mCompactionThread = null;
- mCompactionHandler = null;
+ mAppCompact = null;
mHiddenApiBlacklist = null;
mFactoryTest = FACTORY_TEST_OFF;
}
@@ -2265,95 +2259,6 @@
mProcStartHandlerThread.start();
mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
- mCompactionThread = new ServiceThread("CompactionThread",
- THREAD_PRIORITY_FOREGROUND, true);
- mCompactionThread.start();
- mCompactionHandler = new Handler(mCompactionThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case COMPACT_PROCESS_MSG: {
- long start = SystemClock.uptimeMillis();
- ProcessRecord proc;
- int pid;
- String action;
- final String name;
- int pendingAction, lastCompactAction;
- long lastCompactTime;
- synchronized(ActivityManagerService.this) {
- proc = mPendingCompactionProcesses.remove(0);
-
- // don't compact if the process has returned to perceptible
- if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
- return;
- }
-
- pid = proc.pid;
- name = proc.processName;
- pendingAction = proc.reqCompactAction;
- lastCompactAction = proc.lastCompactAction;
- lastCompactTime = proc.lastCompactTime;
- }
- if (pid == 0) {
- // not a real process, either one being launched or one being killed
- return;
- }
-
- // basic throttling
- if (pendingAction == COMPACT_PROCESS_SOME) {
- // if we're compacting some, then compact if >10s after last full
- // or >5s after last some
- if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) ||
- (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000)))
- return;
- } else {
- // if we're compacting full, then compact if >10s after last full
- // or >.5s after last some
- if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) ||
- (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000)))
- return;
- }
-
- try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " +
- ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") +
- ": " + name);
- long[] rssBefore = Process.getRss(pid);
- FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
- if (pendingAction == COMPACT_PROCESS_SOME) {
- action = "file";
- } else {
- action = "all";
- }
- fos.write(action.getBytes());
- fos.close();
- long[] rssAfter = Process.getRss(pid);
- long end = SystemClock.uptimeMillis();
- long time = end - start;
- EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
- rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
- lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
- StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
- rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
- lastCompactAction, lastCompactTime, msg.arg1,
- ActivityManager.processStateAmToProto(msg.arg2));
- synchronized(ActivityManagerService.this) {
- proc.lastCompactTime = end;
- proc.lastCompactAction = pendingAction;
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } catch (Exception e) {
- // nothing to do, presumably the process died
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
- }
- }
- }
- };
-
-
mConstants = new ActivityManagerConstants(this, mHandler);
mProcessList.init(this);
@@ -2408,6 +2313,8 @@
DisplayThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mAppCompact = new AppCompactor(this);
+
mProcessCpuThread = new Thread("CpuTracker") {
@Override
public void run() {
@@ -2454,7 +2361,7 @@
try {
Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
Process.THREAD_GROUP_SYSTEM);
- Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(),
+ Process.setThreadGroupAndCpuset(mAppCompact.mCompactionThread.getThreadId(),
Process.THREAD_GROUP_SYSTEM);
} catch (Exception e) {
Slog.w(TAG, "Setting background thread cpuset failed");
@@ -15711,7 +15618,7 @@
}
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
- boolean doingAll, long now) {
+ boolean doingAll, long now, boolean cycleReEval) {
if (mAdjSeq == app.adjSeq) {
if (app.adjSeq == app.completedAdjSeq) {
// This adjustment has already been computed successfully.
@@ -15777,20 +15684,21 @@
app.systemNoUi = false;
}
if (!app.systemNoUi) {
- if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
- // screen on, promote UI
- app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
- } else {
- // screen off, restrict UI scheduling
- app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
- }
+ if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ // screen on, promote UI
+ app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+ } else {
+ // screen off, restrict UI scheduling
+ app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+ }
}
+ app.setCurRawProcState(app.getCurProcState());
app.curAdj = app.maxAdj;
app.completedAdjSeq = app.adjSeq;
// if curAdj is less than prevAppAdj, then this process was promoted
- return app.curAdj < prevAppAdj;
+ return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
}
app.systemNoUi = false;
@@ -16032,8 +15940,13 @@
// By default, we use the computed adjustment. It may be changed if
// there are applications dependent on our services or providers, but
// this gives us a baseline and makes sure we don't get into an
- // infinite recursion.
- app.setCurRawAdj(adj);
+ // infinite recursion. If we're re-evaluating due to cycles, use the previously computed
+ // values.
+ app.setCurRawAdj(!cycleReEval ? adj : Math.min(adj, app.getCurRawAdj()));
+ app.setCurRawProcState(!cycleReEval
+ ? procState
+ : Math.min(procState, app.getCurRawProcState()));
+
app.hasStartedServices = false;
app.adjSeq = mAdjSeq;
@@ -16135,21 +16048,15 @@
boolean trackedProcState = false;
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
- computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
- if (client.containsCycle) {
- // We've detected a cycle. We should retry computeOomAdjLocked later in
- // case a later-checked connection from a client would raise its
- // priority legitimately.
- app.containsCycle = true;
- // If the client has not been completely evaluated, skip using its
- // priority. Else use the conservative value for now and look for a
- // better state in the next iteration.
- if (client.completedAdjSeq < mAdjSeq) {
- continue;
- }
+ computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+ if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+ continue;
}
+
int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurProcState();
+ int clientProcState = client.getCurRawProcState();
+
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
@@ -16234,6 +16141,7 @@
}
if (adj > newAdj) {
adj = newAdj;
+ app.setCurRawAdj(adj);
adjType = "service";
}
}
@@ -16305,6 +16213,7 @@
}
if (procState > clientProcState) {
procState = clientProcState;
+ app.setCurRawProcState(procState);
if (adjType == null) {
adjType = "service";
}
@@ -16336,6 +16245,7 @@
if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
&& a.isActivityVisible()) {
adj = ProcessList.FOREGROUND_APP_ADJ;
+ app.setCurRawAdj(adj);
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -16377,21 +16287,15 @@
// Being our own client is not interesting.
continue;
}
- computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
- if (client.containsCycle) {
- // We've detected a cycle. We should retry computeOomAdjLocked later in
- // case a later-checked connection from a client would raise its
- // priority legitimately.
- app.containsCycle = true;
- // If the client has not been completely evaluated, skip using its
- // priority. Else use the conservative value for now and look for a
- // better state in the next iteration.
- if (client.completedAdjSeq < mAdjSeq) {
- continue;
- }
+ computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+ if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+ continue;
}
+
int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurProcState();
+ int clientProcState = client.getCurRawProcState();
+
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
@@ -16405,6 +16309,7 @@
} else {
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+ app.setCurRawAdj(adj);
adjType = "provider";
}
app.cached &= client.cached;
@@ -16440,6 +16345,7 @@
conn.trackProcState(clientProcState, mAdjSeq, now);
if (procState > clientProcState) {
procState = clientProcState;
+ app.setCurRawProcState(procState);
}
if (client.getCurrentSchedulingGroup() > schedGroup) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -16465,6 +16371,7 @@
if (cpr.hasExternalProcessHandles()) {
if (adj > ProcessList.FOREGROUND_APP_ADJ) {
adj = ProcessList.FOREGROUND_APP_ADJ;
+ app.setCurRawAdj(adj);
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.adjType = "ext-provider";
@@ -16476,6 +16383,7 @@
}
if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ app.setCurRawProcState(procState);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to external provider: " + app);
@@ -16620,6 +16528,7 @@
app.curAdj = app.modifyRawOomAdj(adj);
app.setCurrentSchedulingGroup(schedGroup);
app.setCurProcState(procState);
+ app.setCurRawProcState(procState);
app.setHasForegroundActivities(foregroundActivities);
app.completedAdjSeq = mAdjSeq;
@@ -16627,6 +16536,44 @@
return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
}
+ /**
+ * Checks if for the given app and client, there's a cycle that should skip over the client
+ * for now or use partial values to evaluate the effect of the client binding.
+ * @param app
+ * @param client
+ * @param procState procstate evaluated so far for this app
+ * @param adj oom_adj evaluated so far for this app
+ * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
+ * evaluation.
+ * @return whether to skip using the client connection at this time
+ */
+ private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+ int procState, int adj, boolean cycleReEval) {
+ if (client.containsCycle) {
+ // We've detected a cycle. We should retry computeOomAdjLocked later in
+ // case a later-checked connection from a client would raise its
+ // priority legitimately.
+ app.containsCycle = true;
+ // If the client has not been completely evaluated, check if it's worth
+ // using the partial values.
+ if (client.completedAdjSeq < mAdjSeq) {
+ if (cycleReEval) {
+ // If the partial values are no better, skip until the next
+ // attempt
+ if (client.getCurRawProcState() >= procState
+ && client.getCurRawAdj() >= adj) {
+ return true;
+ }
+ // Else use the client's partial procstate and adj to adjust the
+ // effect of the binding
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private static final class RecordPssRunnable implements Runnable {
private final ActivityManagerService mService;
private final ProcessRecord mProc;
@@ -17043,18 +16990,10 @@
if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
(app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
app.curAdj == ProcessList.HOME_APP_ADJ)) {
- app.reqCompactAction = COMPACT_PROCESS_SOME;
- mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendMessage(
- mCompactionHandler.obtainMessage(
- COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+ mAppCompact.compactAppSome(app);
} else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- app.reqCompactAction = COMPACT_PROCESS_FULL;
- mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendMessage(
- mCompactionHandler.obtainMessage(
- COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+ mAppCompact.compactAppFull(app);
}
}
ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
@@ -17493,7 +17432,7 @@
return false;
}
- computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
+ computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
}
@@ -17868,12 +17807,14 @@
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
app.containsCycle = false;
+ app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+ app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
}
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
- computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+ computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now, false);
// if any app encountered a cycle, we need to perform an additional loop later
retryCycles |= app.containsCycle;
@@ -17976,8 +17917,8 @@
for (int i=0; i<N; i++) {
ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-
- if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
+ if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now,
+ true)) {
retryCycles = true;
}
}
@@ -20074,18 +20015,18 @@
}
@Override
- public int checkOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl) {
+ public int checkOperation(int code, int uid, String packageName, boolean raw,
+ QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code, Process.SHELL_UID,
- "com.android.shell");
+ "com.android.shell", raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, uid, packageName);
+ return superImpl.apply(code, uid, packageName, raw);
}
@Override
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
new file mode 100644
index 0000000..aee16c3
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -0,0 +1,187 @@
+/*
+ * 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.am;
+
+import com.android.internal.annotations.GuardedBy;
+
+import android.app.ActivityManager;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+
+import android.util.EventLog;
+import android.util.StatsLog;
+
+import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
+
+import com.android.server.ServiceThread;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public final class AppCompactor {
+ /**
+ * Processes to compact.
+ */
+ final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>();
+
+ /*
+ * This thread must be moved to the system background cpuset.
+ * If that doesn't happen, it's probably going to draw a lot of power.
+ * However, this has to happen after the first updateOomAdjLocked, because
+ * that will wipe out the cpuset assignment for system_server threads.
+ * Accordingly, this is in the AMS constructor.
+ */
+ final ServiceThread mCompactionThread;
+
+ static final int COMPACT_PROCESS_SOME = 1;
+ static final int COMPACT_PROCESS_FULL = 2;
+ static final int COMPACT_PROCESS_MSG = 1;
+ final Handler mCompactionHandler;
+
+ final ActivityManagerService mAm;
+ final ActivityManagerConstants mConstants;
+
+ public AppCompactor(ActivityManagerService am) {
+ mAm = am;
+ mConstants = am.mConstants;
+
+ mCompactionThread = new ServiceThread("CompactionThread",
+ THREAD_PRIORITY_FOREGROUND, true);
+ mCompactionThread.start();
+ mCompactionHandler = new MemCompactionHandler(this);
+ }
+
+ // Must be called while holding AMS lock.
+ final void compactAppSome(ProcessRecord app) {
+ app.reqCompactAction = COMPACT_PROCESS_SOME;
+ mPendingCompactionProcesses.add(app);
+ mCompactionHandler.sendMessage(
+ mCompactionHandler.obtainMessage(
+ COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+ }
+
+ // Must be called while holding AMS lock.
+ final void compactAppFull(ProcessRecord app) {
+ app.reqCompactAction = COMPACT_PROCESS_FULL;
+ mPendingCompactionProcesses.add(app);
+ mCompactionHandler.sendMessage(
+ mCompactionHandler.obtainMessage(
+ COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+
+ }
+ final class MemCompactionHandler extends Handler {
+ AppCompactor mAc;
+
+ private MemCompactionHandler(AppCompactor ac) {
+ super(ac.mCompactionThread.getLooper());
+ mAc = ac;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case COMPACT_PROCESS_MSG: {
+ long start = SystemClock.uptimeMillis();
+ ProcessRecord proc;
+ int pid;
+ String action;
+ final String name;
+ int pendingAction, lastCompactAction;
+ long lastCompactTime;
+ synchronized(mAc.mAm) {
+ proc = mAc.mPendingCompactionProcesses.remove(0);
+
+ // don't compact if the process has returned to perceptible
+ if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
+ return;
+ }
+
+ pid = proc.pid;
+ name = proc.processName;
+ pendingAction = proc.reqCompactAction;
+ lastCompactAction = proc.lastCompactAction;
+ lastCompactTime = proc.lastCompactTime;
+ }
+ if (pid == 0) {
+ // not a real process, either one being launched or one being killed
+ return;
+ }
+
+ // basic throttling
+ if (pendingAction == COMPACT_PROCESS_SOME) {
+ // if we're compacting some, then compact if >10s after last full
+ // or >5s after last some
+ if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) ||
+ (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+ return;
+ }
+ } else {
+ // if we're compacting full, then compact if >10s after last full
+ // or >.5s after last some
+ if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) ||
+ (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+ return;
+ }
+ }
+
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " +
+ ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") +
+ ": " + name);
+ long[] rssBefore = Process.getRss(pid);
+ FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
+ if (pendingAction == COMPACT_PROCESS_SOME) {
+ action = "file";
+ } else {
+ action = "all";
+ }
+ fos.write(action.getBytes());
+ fos.close();
+ long[] rssAfter = Process.getRss(pid);
+ long end = SystemClock.uptimeMillis();
+ long time = end - start;
+ EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
+ rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
+ StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
+ rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1,
+ ActivityManager.processStateAmToProto(msg.arg2));
+ synchronized(mAc.mAm) {
+ proc.lastCompactTime = end;
+ proc.lastCompactAction = pendingAction;
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ } catch (Exception e) {
+ // nothing to do, presumably the process died
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 5208ca5..e483b26 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -15,7 +15,7 @@
jjaggi@google.com
racarr@google.com
chaviw@google.com
-brycelee@google.com
+vishnun@google.com
akulian@google.com
roosa@google.com
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c4b7150..c15b7c7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -153,6 +153,7 @@
int trimMemoryLevel; // Last selected memory trimming level
private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+ private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
int pssStatType; // The type of stat collection that we are currently requesting
@@ -902,6 +903,7 @@
if (mRepProcState > newState) {
mRepProcState = newState;
setCurProcState(newState);
+ setCurRawProcState(newState);
for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
uid, processName, pkgList.keyAt(ipkg),
@@ -984,6 +986,14 @@
return mCurProcState;
}
+ void setCurRawProcState(int curRawProcState) {
+ mCurRawProcState = curRawProcState;
+ }
+
+ int getCurRawProcState() {
+ return mCurRawProcState;
+ }
+
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 2feea41..905f826 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -20,11 +20,11 @@
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioSystem;
import android.media.IRecordingConfigDispatcher;
import android.media.MediaRecorder;
+import android.media.audiofx.AudioEffect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -64,12 +64,19 @@
* Implementation of android.media.AudioSystem.AudioRecordingCallback
*/
public void onRecordingConfigurationChanged(int event, int uid, int session, int source,
- int[] recordingInfo, String packName) {
+ int portId, boolean silenced, int[] recordingInfo,
+ AudioEffect.Descriptor[] clientEffects,
+ AudioEffect.Descriptor[] effects,
+ int activeSource, String packName) {
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
+ String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name;
+ String effectName = effects.length == 0 ? "None" : effects[0].name;
+
final List<AudioRecordingConfiguration> configsSystem =
- updateSnapshot(event, uid, session, source, recordingInfo);
+ updateSnapshot(event, uid, session, source, recordingInfo,
+ portId, silenced, activeSource, clientEffects, effects);
if (configsSystem != null){
synchronized (mClients) {
// list of recording configurations for "public consumption". It is only computed if
@@ -179,13 +186,20 @@
* @param session
* @param source
* @param recordingFormat see
- * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
+ * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int,\
+ int, int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
* for the definition of the contents of the array
+ * @param portId
+ * @param silenced
+ * @param activeSource
+ * @param clientEffects
+ * @param effects
* @return null if the list of active recording sessions has not been modified, a list
* with the current active configurations otherwise.
*/
private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,
- int source, int[] recordingInfo) {
+ int source, int[] recordingInfo, int portId, boolean silenced, int activeSource,
+ AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects) {
final boolean configChanged;
final ArrayList<AudioRecordingConfiguration> configs;
synchronized(mRecordConfigs) {
@@ -211,7 +225,7 @@
.setSampleRate(recordingInfo[5])
.build();
final int patchHandle = recordingInfo[6];
- final Integer sessionKey = new Integer(session);
+ final Integer portIdKey = new Integer(portId);
final String[] packages = mPackMan.getPackagesForUid(uid);
final String packageName;
@@ -222,19 +236,20 @@
}
final AudioRecordingConfiguration updatedConfig =
new AudioRecordingConfiguration(uid, session, source,
- clientFormat, deviceFormat, patchHandle, packageName);
+ clientFormat, deviceFormat, patchHandle, packageName,
+ portId, silenced, activeSource, clientEffects, effects);
- if (mRecordConfigs.containsKey(sessionKey)) {
- if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
+ if (mRecordConfigs.containsKey(portIdKey)) {
+ if (updatedConfig.equals(mRecordConfigs.get(portIdKey))) {
configChanged = false;
} else {
// config exists but has been modified
- mRecordConfigs.remove(sessionKey);
- mRecordConfigs.put(sessionKey, updatedConfig);
+ mRecordConfigs.remove(portIdKey);
+ mRecordConfigs.put(portIdKey, updatedConfig);
configChanged = true;
}
} else {
- mRecordConfigs.put(sessionKey, updatedConfig);
+ mRecordConfigs.put(portIdKey, updatedConfig);
configChanged = true;
}
if (configChanged) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 1882be2..a381477 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -653,7 +653,7 @@
}
mHandler.post(() -> {
- final Pair<Integer, Integer> result = checkAndGetBiometricModality(callingUserId);
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
final int modality = result.first;
final int error = result.second;
@@ -950,7 +950,7 @@
* {@link BiometricAuthenticator#TYPE_FACE}
* and the error containing one of the {@link BiometricConstants} errors.
*/
- private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
+ private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
int modality = TYPE_NONE;
// No biometric features, send error
@@ -979,7 +979,7 @@
// order.
firstHwAvailable = modality;
}
- if (authenticator.hasEnrolledTemplates(callingUid)) {
+ if (authenticator.hasEnrolledTemplates(userId)) {
hasTemplatesEnrolled = true;
if (isEnabledForApp(modality)) {
// TODO(b/110907543): When face settings (and other settings) have both a
diff --git a/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
new file mode 100644
index 0000000..cc319bf
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
@@ -0,0 +1,135 @@
+/*
+ * 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.deviceidle;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.DeviceIdleController;
+
+/**
+ * Track whether there are any active Bluetooth devices connected.
+ */
+public class BluetoothConstraint implements IDeviceIdleConstraint {
+ private static final String TAG = BluetoothConstraint.class.getSimpleName();
+ private static final long INACTIVITY_TIMEOUT_MS = 20 * 60 * 1000L;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final DeviceIdleController.LocalService mLocalService;
+ private final BluetoothManager mBluetoothManager;
+
+ private volatile boolean mConnected = true;
+ private volatile boolean mMonitoring = false;
+
+ public BluetoothConstraint(
+ Context context, Handler handler, DeviceIdleController.LocalService localService) {
+ mContext = context;
+ mHandler = handler;
+ mLocalService = localService;
+ mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
+ }
+
+ @Override
+ public synchronized void startMonitoring() {
+ // Start by assuming we have a connected bluetooth device.
+ mConnected = true;
+ mMonitoring = true;
+
+ // Register a receiver to get updates on bluetooth devices disconnecting or the
+ // adapter state changing.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+
+ // Some devices will try to stay connected indefinitely. Set a timeout to ignore them.
+ mHandler.sendMessageDelayed(
+ Message.obtain(mHandler, mTimeoutCallback), INACTIVITY_TIMEOUT_MS);
+
+ // Now we have the receiver registered, make a direct check for connected devices.
+ updateAndReportActiveLocked();
+ }
+
+ @Override
+ public synchronized void stopMonitoring() {
+ mContext.unregisterReceiver(mReceiver);
+ mHandler.removeCallbacks(mTimeoutCallback);
+ mMonitoring = false;
+ }
+
+ private synchronized void cancelMonitoringDueToTimeout() {
+ if (mMonitoring) {
+ mMonitoring = false;
+ mLocalService.onConstraintStateChanged(this, /* active= */ false);
+ }
+ }
+
+ /**
+ * Check the latest data from BluetoothManager and let DeviceIdleController know whether we
+ * have connected devices (for example TV remotes / gamepads) and thus want to stay awake.
+ */
+ @GuardedBy("this")
+ private void updateAndReportActiveLocked() {
+ final boolean connected = isBluetoothConnected(mBluetoothManager);
+ if (connected != mConnected) {
+ mConnected = connected;
+ // If we lost all of our connections, we are on track to going into idle state.
+ mLocalService.onConstraintStateChanged(this, /* active= */ mConnected);
+ }
+ }
+
+ /**
+ * True if the bluetooth adapter exists, is enabled, and has at least one GATT device connected.
+ */
+ @VisibleForTesting
+ static boolean isBluetoothConnected(BluetoothManager bluetoothManager) {
+ BluetoothAdapter adapter = bluetoothManager.getAdapter();
+ if (adapter != null && adapter.isEnabled()) {
+ return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT).size() > 0;
+ }
+ return false;
+ }
+
+ /**
+ * Registered in {@link #startMonitoring()}, unregistered in {@link #stopMonitoring()}.
+ */
+ @VisibleForTesting
+ final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(intent.getAction())) {
+ mLocalService.exitIdle("bluetooth");
+ } else {
+ updateAndReportActiveLocked();
+ }
+ }
+ };
+
+ private final Runnable mTimeoutCallback = () -> cancelMonitoringDueToTimeout();
+}
diff --git a/services/core/java/com/android/server/deviceidle/ConstraintController.java b/services/core/java/com/android/server/deviceidle/ConstraintController.java
new file mode 100644
index 0000000..6d52f71
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/ConstraintController.java
@@ -0,0 +1,32 @@
+/*
+ * 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.deviceidle;
+
+/**
+ * Device idle constraints for a specific form factor or use-case.
+ */
+public interface ConstraintController {
+ /**
+ * Begin any general continuing work and register all constraints.
+ */
+ void start();
+
+ /**
+ * Unregister all constraints and stop any general work.
+ */
+ void stop();
+}
diff --git a/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java
new file mode 100644
index 0000000..4d5760e
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java
@@ -0,0 +1,57 @@
+/*
+ * 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.deviceidle;
+
+/**
+ * Current state of an {@link IDeviceIdleConstraint}.
+ *
+ * If the current doze state is between leastActive and mostActive, then startMonitoring() will
+ * be the most recent call. Otherwise, stopMonitoring() is the most recent call.
+ */
+public class DeviceIdleConstraintTracker {
+
+ /**
+ * Appears in "dumpsys deviceidle".
+ */
+ public final String name;
+
+ /**
+ * Whenever a constraint is active, it will keep the device at or above
+ * minState (provided the rule is currently in effect).
+ *
+ */
+ public final int minState;
+
+ /**
+ * Whether this constraint currently prevents going below {@link #minState}.
+ *
+ * When the state is set to exactly minState, active is automatically
+ * overwritten with {@code true}.
+ */
+ public boolean active = false;
+
+ /**
+ * Internal tracking for whether the {@link IDeviceIdleConstraint} on the other
+ * side has been told it needs to send updates.
+ */
+ public boolean monitoring = false;
+
+ public DeviceIdleConstraintTracker(final String name, int minState) {
+ this.name = name;
+ this.minState = minState;
+ }
+}
diff --git a/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java
new file mode 100644
index 0000000..f1f95730
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java
@@ -0,0 +1,67 @@
+/*
+ * 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.deviceidle;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Implemented by OEM and/or Form Factor. System ones are built into the
+ * image regardless of build flavour but may still be switched off at run time.
+ * Individual feature flags at build time control which are used. We may
+ * also explore a local override for quick testing.
+ */
+public interface IDeviceIdleConstraint {
+
+ /**
+ * A state for this constraint to block descent from.
+ *
+ * <p>These states are a subset of the states in DeviceIdleController that make sense for
+ * constraints to be able to block on. For example, {@link #SENSING_OR_ABOVE} clearly has
+ * defined "above" and "below" states. However, a hypothetical {@code QUICK_DOZE_OR_ABOVE}
+ * state would not have clear semantics as to what transitions should be blocked and which
+ * should be allowed.
+ */
+ @IntDef(flag = false, value = {
+ ACTIVE,
+ SENSING_OR_ABOVE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface MinimumState {}
+
+ int ACTIVE = 0;
+ int SENSING_OR_ABOVE = 1;
+
+ /**
+ * Begin tracking events for this constraint.
+ *
+ * <p>The device idle controller has reached a point where it is waiting for the all-clear
+ * from this tracker (possibly among others) in order to continue with progression into
+ * idle state. It will not proceed until one of the following happens:
+ * <ul>
+ * <li>The constraint reports inactive with {@code .setActive(false)}.</li>
+ * <li>The constraint is unregistered with {@code .unregisterDeviceIdleConstraint(this)}.</li>
+ * <li>A transition timeout in DeviceIdleController fires.
+ * </ul>
+ */
+ void startMonitoring();
+
+ /** Stop checking for new events and do not call into LocalService with updates any more. */
+ void stopMonitoring();
+}
diff --git a/services/core/java/com/android/server/deviceidle/TvConstraintController.java b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
new file mode 100644
index 0000000..2d472de6
--- /dev/null
+++ b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
@@ -0,0 +1,66 @@
+/*
+ * 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.deviceidle;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
+
+/**
+ * Device idle constraints for television devices.
+ *
+ * <p>Televisions are devices with {@code FEATURE_LEANBACK_ONLY}. Other devices might support
+ * some kind of leanback mode but they should not follow the same rules for idle state.
+ */
+public class TvConstraintController implements ConstraintController {
+ private final Context mContext;
+ private final Handler mHandler;
+ private final DeviceIdleController.LocalService mDeviceIdleService;
+
+ @Nullable
+ private final BluetoothConstraint mBluetoothConstraint;
+
+ public TvConstraintController(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ mDeviceIdleService = LocalServices.getService(DeviceIdleController.LocalService.class);
+
+ final PackageManager pm = context.getPackageManager();
+ mBluetoothConstraint = pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+ ? new BluetoothConstraint(mContext, mHandler, mDeviceIdleService)
+ : null;
+ }
+
+ @Override
+ public void start() {
+ if (mBluetoothConstraint != null) {
+ mDeviceIdleService.registerDeviceIdleConstraint(
+ mBluetoothConstraint, "bluetooth", IDeviceIdleConstraint.SENSING_OR_ABOVE);
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (mBluetoothConstraint != null) {
+ mDeviceIdleService.unregisterDeviceIdleConstraint(mBluetoothConstraint);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 78b3c15..52eccca 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,6 +17,13 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -27,6 +34,8 @@
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.util.EventLog;
@@ -34,14 +43,15 @@
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
class AutomaticBrightnessController {
private static final String TAG = "AutomaticBrightnessController";
- private static final boolean DEBUG = false;
private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
// If true, enables the use of the screen auto-brightness adjustment setting.
@@ -66,6 +76,8 @@
private static final int MSG_UPDATE_AMBIENT_LUX = 1;
private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
+ private static final int MSG_UPDATE_FOREGROUND_APP = 4;
+ private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
@@ -126,6 +138,8 @@
private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
+ private boolean mLoggingEnabled;
+
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
// May be 0 if no warm-up is required.
@@ -192,6 +206,19 @@
private float mShortTermModelAnchor;
private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
+ // Context-sensitive brightness configurations require keeping track of the foreground app's
+ // package name and category, which is done by registering a TaskStackListener to call back to
+ // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
+ // package namd and PackageManager to get its category (so might as well cache them).
+ private int mUserId;
+ private String mForegroundAppPackageName;
+ private String mPendingForegroundAppPackageName;
+ private @ApplicationInfo.Category int mForegroundAppCategory;
+ private @ApplicationInfo.Category int mPendingForegroundAppCategory;
+ private TaskStackListenerImpl mTaskStackListener;
+ private IActivityTaskManager mActivityTaskManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
SensorManager sensorManager, BrightnessMappingStrategy mapper,
int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -226,6 +253,42 @@
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
+
+ mUserId = ActivityManager.getCurrentUser();
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mTaskStackListener = new TaskStackListenerImpl();
+ mForegroundAppPackageName = null;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Update the current user's ID.
+ *
+ * @param userId
+ * The current user's ID.
+ */
+ public void onSwitchUser(int userId) {
+ mUserId = userId;
}
public int getAutomaticScreenBrightness() {
@@ -290,7 +353,7 @@
}
final int oldPolicy = mDisplayPolicy;
mDisplayPolicy = policy;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
}
if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
@@ -317,7 +380,7 @@
mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
mShortTermModelValid = true;
mShortTermModelAnchor = mAmbientLux;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
}
return true;
@@ -330,7 +393,7 @@
}
private void invalidateShortTermModel() {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: invalidate user data");
}
mShortTermModelValid = false;
@@ -383,7 +446,11 @@
pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
pw.println(" mBrightnessAdjustmentSampleOldBrightness="
+ mBrightnessAdjustmentSampleOldBrightness);
- pw.println(" mShortTermModelValid=" + mShortTermModelValid);
+ pw.println(" mUserId=" + mUserId);
+ pw.println(" mForegroundAppPackageName=" + mForegroundAppPackageName);
+ pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+ pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
+ pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
pw.println();
mBrightnessMapper.dump(pw);
@@ -399,6 +466,7 @@
mLightSensorEnabled = true;
mLightSensorEnableTime = SystemClock.uptimeMillis();
mCurrentLightSensorRate = mInitialLightSensorRate;
+ registerForegroundAppUpdater();
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
mCurrentLightSensorRate * 1000, mHandler);
return true;
@@ -411,6 +479,7 @@
mAmbientLightRingBuffer.clear();
mCurrentLightSensorRate = -1;
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ unregisterForegroundAppUpdater();
mSensorManager.unregisterListener(mLightSensorListener);
}
return false;
@@ -441,7 +510,7 @@
private void adjustLightSensorRate(int lightSensorRate) {
// if the light sensor rate changed, update the sensor listener
if (lightSensorRate != mCurrentLightSensorRate) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "adjustLightSensorRate: " +
"previousRate=" + mCurrentLightSensorRate + ", " +
"currentRate=" + lightSensorRate);
@@ -458,7 +527,7 @@
}
private void setAmbientLux(float lux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAmbientLux(" + lux + ")");
}
if (lux < 0) {
@@ -476,7 +545,7 @@
final float maxAmbientLux =
mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
}
@@ -490,7 +559,7 @@
}
private float calculateAmbientLux(long now, long horizon) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
}
final int N = mAmbientLightRingBuffer.size();
@@ -509,7 +578,7 @@
break;
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
mAmbientLightRingBuffer.getTime(endIndex) + ", " +
mAmbientLightRingBuffer.getLux(endIndex) + ")");
@@ -527,7 +596,7 @@
final long startTime = eventTime - now;
float weight = calculateWeight(startTime, endTime);
float lux = mAmbientLightRingBuffer.getLux(i);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
"lux=" + lux + ", " +
"weight=" + weight);
@@ -536,7 +605,7 @@
sum += lux * weight;
endTime = startTime;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: " +
"totalWeight=" + totalWeight + ", " +
"newAmbientLux=" + (sum / totalWeight));
@@ -591,7 +660,7 @@
final long timeWhenSensorWarmedUp =
mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
if (time < timeWhenSensorWarmedUp) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " +
"time=" + time + ", " +
"timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
@@ -602,7 +671,7 @@
}
setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
mAmbientLuxValid = true;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Initializing: " +
"mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
"mAmbientLux=" + mAmbientLux);
@@ -630,10 +699,10 @@
&& fastAmbientLux <= mAmbientDarkeningThreshold
&& nextDarkenTransition <= time)) {
setAmbientLux(fastAmbientLux);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
+ ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
- + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+ + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+ "mAmbientLux=" + mAmbientLux);
}
@@ -650,7 +719,7 @@
// weighted ambient lux or not.
nextTransitionTime =
nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
}
@@ -662,7 +731,8 @@
return;
}
- float value = mBrightnessMapper.getBrightness(mAmbientLux);
+ float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+ mForegroundAppCategory);
int newScreenAutoBrightness =
clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
@@ -673,7 +743,7 @@
if (mScreenAutoBrightness != -1
&& newScreenAutoBrightness > mScreenDarkeningThreshold
&& newScreenAutoBrightness < mScreenBrighteningThreshold) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
+ " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
}
@@ -681,8 +751,7 @@
}
if (mScreenAutoBrightness != newScreenAutoBrightness) {
-
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAutoBrightness: " +
"mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
"newScreenAutoBrightness=" + newScreenAutoBrightness);
@@ -718,18 +787,11 @@
BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
}
- private void cancelBrightnessAdjustmentSample() {
- if (mBrightnessAdjustmentSamplePending) {
- mBrightnessAdjustmentSamplePending = false;
- mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
- }
- }
-
private void collectBrightnessAdjustmentSample() {
if (mBrightnessAdjustmentSamplePending) {
mBrightnessAdjustmentSamplePending = false;
if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
"lux=" + mAmbientLux + ", " +
"brightness=" + mScreenAutoBrightness + ", " +
@@ -745,6 +807,68 @@
}
}
+ // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
+ // foreground app's package name and category and correct the brightness accordingly.
+ private void registerForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ // This will not get called until the foreground app changes for the first time, so
+ // call it explicitly to get the current foreground app's info.
+ updateForegroundApp();
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ }
+
+ private void unregisterForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ mForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ // Set the foreground app's package name and category, so brightness can be corrected per app.
+ private void updateForegroundApp() {
+ // The ActivityTaskManager's lock tends to get contended, so this is done in a background
+ // thread and applied via this thread's handler synchronously.
+ BackgroundThread.getHandler().post(new Runnable() {
+ public void run() {
+ try {
+ // The foreground app is the top activity of the focused tasks stack.
+ final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
+ if (info == null || info.topActivity == null) {
+ return;
+ }
+ final String packageName = info.topActivity.getPackageName();
+ // If the app didn't change, there's nothing to do. Otherwise, we have to
+ // update the category and re-apply the brightness correction.
+ if (mForegroundAppPackageName != null
+ && mForegroundAppPackageName.equals(packageName)) {
+ return;
+ }
+ mPendingForegroundAppPackageName = packageName;
+ ApplicationInfo app = mPackageManagerInternal.getApplicationInfo(packageName,
+ 0 /* flags */, Process.SYSTEM_UID /* filterCallingUid */, mUserId);
+ mPendingForegroundAppCategory = app.category;
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ }
+ });
+ }
+
+ private void updateForegroundAppSync() {
+ mForegroundAppPackageName = mPendingForegroundAppPackageName;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = mPendingForegroundAppCategory;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ updateAutoBrightness(true /* sendUpdate */);
+ }
+
private final class AutomaticBrightnessHandler extends Handler {
public AutomaticBrightnessHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -764,6 +888,14 @@
case MSG_INVALIDATE_SHORT_TERM_MODEL:
invalidateShortTermModel();
break;
+
+ case MSG_UPDATE_FOREGROUND_APP:
+ updateForegroundApp();
+ break;
+
+ case MSG_UPDATE_FOREGROUND_APP_SYNC:
+ updateForegroundAppSync();
+ break;
}
}
}
@@ -784,6 +916,15 @@
}
};
+ // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
+ // moving to top.
+ class TaskStackListenerImpl extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
+ }
+ }
+
/** Callbacks to request updates to the display's power state. */
interface Callbacks {
void updateBrightness();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 76c191d..9fce644 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,9 +17,11 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessCorrection;
import android.os.PowerManager;
import android.util.MathUtils;
import android.util.Pair;
@@ -42,11 +44,12 @@
*/
public abstract class BrightnessMappingStrategy {
private static final String TAG = "BrightnessMappingStrategy";
- private static final boolean DEBUG = false;
private static final float LUX_GRAD_SMOOTHING = 0.25f;
private static final float MAX_GRAD = 1.0f;
+ protected boolean mLoggingEnabled;
+
private static final Plog PLOG = Plog.createSystemPlog(TAG);
@Nullable
@@ -161,6 +164,22 @@
}
/**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
* Sets the {@link BrightnessConfiguration}.
*
* @param config The new configuration. If {@code null} is passed, the default configuration is
@@ -170,15 +189,33 @@
public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
/**
- * Returns the desired brightness of the display based on the current ambient lux.
+ * Returns the desired brightness of the display based on the current ambient lux, including
+ * any context-related corrections.
*
* The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
* brightness and 0 is the display at minimum brightness.
*
* @param lux The current ambient brightness in lux.
+ * @param packageName the foreground app package name.
+ * @param category the foreground app package category.
* @return The desired brightness of the display normalized to the range [0, 1.0].
*/
- public abstract float getBrightness(float lux);
+ public abstract float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category);
+
+ /**
+ * Returns the desired brightness of the display based on the current ambient lux.
+ *
+ * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max
+ * brightness and 0 is the display at minimum brightness.
+ *
+ * @param lux The current ambient brightness in lux.
+ *
+ * @return The desired brightness of the display normalized to the range [0, 1.0].
+ */
+ public float getBrightness(float lux) {
+ return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
+ }
/**
* Returns the current auto-brightness adjustment.
@@ -239,13 +276,13 @@
public abstract void dump(PrintWriter pw);
- private static float normalizeAbsoluteBrightness(int brightness) {
+ protected float normalizeAbsoluteBrightness(int brightness) {
brightness = MathUtils.constrain(brightness,
PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
return (float) brightness / PowerManager.BRIGHTNESS_ON;
}
- private static Pair<float[], float[]> insertControlPoint(
+ private Pair<float[], float[]> insertControlPoint(
float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
final int idx = findInsertionPoint(luxLevels, lux);
final float[] newLuxLevels;
@@ -278,7 +315,7 @@
* This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
* than val, then it will return the length of arr as the insertion point.
*/
- private static int findInsertionPoint(float[] arr, float val) {
+ private int findInsertionPoint(float[] arr, float val) {
for (int i = 0; i < arr.length; i++) {
if (val <= arr[i]) {
return i;
@@ -287,8 +324,8 @@
return arr.length;
}
- private static void smoothCurve(float[] lux, float[] brightness, int idx) {
- if (DEBUG) {
+ private void smoothCurve(float[] lux, float[] brightness, int idx) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unsmoothed curve", lux, brightness);
}
float prevLux = lux[idx];
@@ -323,19 +360,19 @@
prevBrightness = newBrightness;
brightness[i] = newBrightness;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("smoothed curve", lux, brightness);
}
}
- private static float permissibleRatio(float currLux, float prevLux) {
+ private float permissibleRatio(float currLux, float prevLux) {
return MathUtils.exp(MAX_GRAD
* (MathUtils.log(currLux + LUX_GRAD_SMOOTHING)
- MathUtils.log(prevLux + LUX_GRAD_SMOOTHING)));
}
- private static float inferAutoBrightnessAdjustment(float maxGamma,
- float desiredBrightness, float currentBrightness) {
+ protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
+ float currentBrightness) {
float adjustment = 0;
float gamma = Float.NaN;
// Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
@@ -355,7 +392,7 @@
adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
}
adjustment = MathUtils.constrain(adjustment, -1, +1);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
@@ -364,16 +401,16 @@
return adjustment;
}
- private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
+ protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
float userLux, float userBrightness, float adjustment, float maxGamma) {
float[] newLux = lux;
float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unadjusted curve", newLux, newBrightness);
}
adjustment = MathUtils.constrain(adjustment, -1, 1);
float gamma = MathUtils.pow(maxGamma, -adjustment);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
}
@@ -382,7 +419,7 @@
newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
}
if (userLux != -1) {
@@ -390,7 +427,7 @@
userBrightness);
newLux = curve.first;
newBrightness = curve.second;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
// This is done for comparison.
curve = insertControlPoint(lux, brightness, userLux, userBrightness);
@@ -440,7 +477,7 @@
mAutoBrightnessAdjustment = 0;
mUserLux = -1;
mUserBrightness = -1;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("simple mapping strategy");
}
computeSpline();
@@ -452,7 +489,8 @@
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
return mSpline.interpolate(lux);
}
@@ -467,7 +505,7 @@
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -485,7 +523,7 @@
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -494,7 +532,7 @@
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -507,7 +545,7 @@
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -614,7 +652,7 @@
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
mDefaultConfig = config;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("physical mapping strategy");
}
mConfig = config;
@@ -629,7 +667,7 @@
if (config.equals(mConfig)) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("brightness configuration");
}
mConfig = config;
@@ -638,9 +676,17 @@
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
float nits = mBrightnessSpline.interpolate(lux);
float backlight = mNitsToBacklightSpline.interpolate(nits);
+ // Correct the brightness according to the current application and its category, but
+ // only if no user data point is set (as this will oevrride the user setting).
+ if (mUserLux == -1) {
+ backlight = correctBrightness(backlight, packageName, category);
+ } else if (mLoggingEnabled) {
+ Slog.d(TAG, "user point set, correction not applied");
+ }
return backlight;
}
@@ -655,7 +701,7 @@
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -673,7 +719,7 @@
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -682,7 +728,7 @@
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -695,7 +741,7 @@
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -758,5 +804,21 @@
Spline spline = Spline.createSpline(curve.first, curve.second);
return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
}
+
+ private float correctBrightness(float brightness, String packageName, int category) {
+ if (packageName != null) {
+ BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
+ BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ return brightness;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 521fa23..b6c82d3 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -39,26 +39,31 @@
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings.Secure;
+import android.provider.Settings.System;
import android.util.MathUtils;
import android.util.Slog;
import android.view.animation.AnimationUtils;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
+import com.android.server.DisplayThread;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.time.DateTimeException;
+import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
/**
* Controls the display's color transforms.
*/
-public final class ColorDisplayService extends SystemService
- implements ColorDisplayController.Callback {
+public final class ColorDisplayService extends SystemService {
private static final String TAG = "ColorDisplayService";
@@ -71,6 +76,7 @@
* The identity matrix, used if one of the given matrices is {@code null}.
*/
private static final float[] MATRIX_IDENTITY = new float[16];
+
static {
Matrix.setIdentityM(MATRIX_IDENTITY, 0);
}
@@ -90,10 +96,12 @@
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
- private ColorDisplayController mController;
+ private ColorDisplayController mNightDisplayController;
+ private ContentObserver mContentObserver;
private ValueAnimator mColorMatrixAnimator;
- private Boolean mIsActivated;
- private AutoMode mAutoMode;
+
+ private Boolean mIsNightDisplayActivated;
+ private NightDisplayAutoMode mNightDisplayAutoMode;
public ColorDisplayService(Context context) {
super(context);
@@ -186,42 +194,102 @@
private void setUp() {
Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
- // Create a new controller for the current user and start listening for changes.
- mController = new ColorDisplayController(getContext(), mCurrentUser);
- mController.setListener(this);
+ mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
+
+ // Listen for external changes to any of the settings.
+ if (mContentObserver == null) {
+ mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ if (setting != null) {
+ switch (setting) {
+ case Secure.NIGHT_DISPLAY_ACTIVATED:
+ onNightDisplayActivated(mNightDisplayController.isActivated());
+ break;
+ case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
+ onNightDisplayColorTemperatureChanged(
+ mNightDisplayController.getColorTemperature());
+ break;
+ case Secure.NIGHT_DISPLAY_AUTO_MODE:
+ onNightDisplayAutoModeChanged(
+ mNightDisplayController.getAutoMode());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
+ onNightDisplayCustomStartTimeChanged(
+ mNightDisplayController.getCustomStartTime());
+ break;
+ case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
+ onNightDisplayCustomEndTimeChanged(
+ mNightDisplayController.getCustomEndTime());
+ break;
+ case System.DISPLAY_COLOR_MODE:
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+ break;
+ case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
+ case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
+ onAccessibilityTransformChanged();
+ break;
+ }
+ }
+ }
+ };
+ }
+ final ContentResolver cr = getContext().getContentResolver();
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(
+ Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ cr.registerContentObserver(
+ Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
// Set the color mode, if valid, and immediately apply the updated tint matrix based on the
// existing activated state. This ensures consistency of tint across the color mode change.
- onDisplayColorModeChanged(mController.getColorMode());
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
// Reset the activated state.
- mIsActivated = null;
+ mIsNightDisplayActivated = null;
setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
// Prepare color transformation matrix.
- setMatrix(mController.getColorTemperature(), mMatrixNight);
+ setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
// Initialize the current auto mode.
- onAutoModeChanged(mController.getAutoMode());
+ onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
// Force the initialization current activated state.
- if (mIsActivated == null) {
- onActivated(mController.isActivated());
+ if (mIsNightDisplayActivated == null) {
+ onNightDisplayActivated(mNightDisplayController.isActivated());
}
}
private void tearDown() {
Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
- if (mController != null) {
- mController.setListener(null);
- mController = null;
+ getContext().getContentResolver().unregisterContentObserver(mContentObserver);
+
+ if (mNightDisplayController != null) {
+ mNightDisplayController = null;
}
- if (mAutoMode != null) {
- mAutoMode.onStop();
- mAutoMode = null;
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStop();
+ mNightDisplayAutoMode = null;
}
if (mColorMatrixAnimator != null) {
@@ -230,67 +298,61 @@
}
}
- @Override
- public void onActivated(boolean activated) {
- if (mIsActivated == null || mIsActivated != activated) {
+ private void onNightDisplayActivated(boolean activated) {
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activated) {
Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
- mIsActivated = activated;
+ mIsNightDisplayActivated = activated;
- if (mAutoMode != null) {
- mAutoMode.onActivated(activated);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onActivated(activated);
}
applyTint(false);
}
}
- @Override
- public void onAutoModeChanged(int autoMode) {
- Slog.d(TAG, "onAutoModeChanged: autoMode=" + autoMode);
+ private void onNightDisplayAutoModeChanged(int autoMode) {
+ Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
- if (mAutoMode != null) {
- mAutoMode.onStop();
- mAutoMode = null;
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStop();
+ mNightDisplayAutoMode = null;
}
if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
- mAutoMode = new CustomAutoMode();
+ mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
} else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
- mAutoMode = new TwilightAutoMode();
+ mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
}
- if (mAutoMode != null) {
- mAutoMode.onStart();
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onStart();
}
}
- @Override
- public void onCustomStartTimeChanged(LocalTime startTime) {
- Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
+ private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
+ Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
- if (mAutoMode != null) {
- mAutoMode.onCustomStartTimeChanged(startTime);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
}
}
- @Override
- public void onCustomEndTimeChanged(LocalTime endTime) {
- Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
+ private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
+ Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
- if (mAutoMode != null) {
- mAutoMode.onCustomEndTimeChanged(endTime);
+ if (mNightDisplayAutoMode != null) {
+ mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
}
}
- @Override
- public void onColorTemperatureChanged(int colorTemperature) {
+ private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
setMatrix(colorTemperature, mMatrixNight);
applyTint(true);
}
- @Override
- public void onDisplayColorModeChanged(int mode) {
+ private void onDisplayColorModeChanged(int mode) {
if (mode == -1) {
return;
}
@@ -301,16 +363,15 @@
}
setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
- setMatrix(mController.getColorTemperature(), mMatrixNight);
+ setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
- dtm.setColorMode(mode, (mIsActivated != null && mIsActivated) ? mMatrixNight
- : MATRIX_IDENTITY);
+ dtm.setColorMode(mode, (mIsNightDisplayActivated != null && mIsNightDisplayActivated)
+ ? mMatrixNight : MATRIX_IDENTITY);
}
- @Override
- public void onAccessibilityTransformChanged(boolean state) {
- onDisplayColorModeChanged(mController.getColorMode());
+ private void onAccessibilityTransformChanged() {
+ onDisplayColorModeChanged(mNightDisplayController.getColorMode());
}
/**
@@ -338,7 +399,7 @@
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
- final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
+ final float[] to = mIsNightDisplayActivated ? mMatrixNight : MATRIX_IDENTITY;
if (immediate) {
dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
@@ -383,7 +444,7 @@
* Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
*
* @param colorTemperature color temperature in Kelvin
- * @param outTemp the 4x4 display transformation matrix for that color temperature
+ * @param outTemp the 4x4 display transformation matrix for that color temperature
*/
private void setMatrix(int colorTemperature, float[] outTemp) {
if (outTemp.length != 16) {
@@ -412,7 +473,8 @@
* @param compareTime the LocalDateTime to compare against
* @return the prior LocalDateTime corresponding to this local time
*/
- public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+ @VisibleForTesting
+ static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
@@ -427,7 +489,8 @@
* @param compareTime the LocalDateTime to compare against
* @return the next LocalDateTime corresponding to this local time
*/
- public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+ @VisibleForTesting
+ static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
@@ -440,13 +503,47 @@
return dtm.isDeviceColorManaged();
}
- private abstract class AutoMode implements ColorDisplayController.Callback {
+ /**
+ * Returns the last time the night display transform activation state was changed, or {@link
+ * LocalDateTime#MIN} if night display has never been activated.
+ */
+ private @NonNull LocalDateTime getNightDisplayLastActivatedTimeSetting() {
+ final ContentResolver cr = getContext().getContentResolver();
+ final String lastActivatedTime = Secure.getStringForUser(
+ cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
+ if (lastActivatedTime != null) {
+ try {
+ return LocalDateTime.parse(lastActivatedTime);
+ } catch (DateTimeParseException ignored) {
+ }
+ // Uses the old epoch time.
+ try {
+ return LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+ ZoneId.systemDefault());
+ } catch (DateTimeException | NumberFormatException ignored) {
+ }
+ }
+ return LocalDateTime.MIN;
+ }
+
+ private abstract class NightDisplayAutoMode {
+
+ public abstract void onActivated(boolean activated);
+
public abstract void onStart();
public abstract void onStop();
+
+ public void onCustomStartTimeChanged(LocalTime startTime) {
+ }
+
+ public void onCustomEndTimeChanged(LocalTime endTime) {
+ }
}
- private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
+ private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
+ AlarmManager.OnAlarmListener {
private final AlarmManager mAlarmManager;
private final BroadcastReceiver mTimeChangedReceiver;
@@ -456,7 +553,7 @@
private LocalDateTime mLastActivatedTime;
- CustomAutoMode() {
+ CustomNightDisplayAutoMode() {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mTimeChangedReceiver = new BroadcastReceiver() {
@Override
@@ -476,15 +573,15 @@
// Maintain the existing activated state if within the current period.
if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
&& (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
- activate = mController.isActivated();
+ activate = mNightDisplayController.isActivated();
}
}
- if (mIsActivated == null || mIsActivated != activate) {
- mController.setActivated(activate);
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ mNightDisplayController.setActivated(activate);
}
- updateNextAlarm(mIsActivated, now);
+ updateNextAlarm(mIsNightDisplayActivated, now);
}
private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
@@ -502,10 +599,10 @@
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
- mStartTime = mController.getCustomStartTime();
- mEndTime = mController.getCustomEndTime();
+ mStartTime = mNightDisplayController.getCustomStartTime();
+ mEndTime = mNightDisplayController.getCustomEndTime();
- mLastActivatedTime = mController.getLastActivatedTime();
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
// Force an update to initialize state.
updateActivated();
@@ -521,7 +618,7 @@
@Override
public void onActivated(boolean activated) {
- mLastActivatedTime = mController.getLastActivatedTime();
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
updateNextAlarm(activated, LocalDateTime.now());
}
@@ -546,11 +643,13 @@
}
}
- private class TwilightAutoMode extends AutoMode implements TwilightListener {
+ private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
+ TwilightListener {
private final TwilightManager mTwilightManager;
+ private LocalDateTime mLastActivatedTime;
- TwilightAutoMode() {
+ TwilightNightDisplayAutoMode() {
mTwilightManager = getLocalService(TwilightManager.class);
}
@@ -562,26 +661,31 @@
}
boolean activate = state.isNight();
- final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
- if (lastActivatedTime != null) {
+ if (mLastActivatedTime != null) {
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime sunrise = state.sunrise();
final LocalDateTime sunset = state.sunset();
// Maintain the existing activated state if within the current period.
- if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
- ^ lastActivatedTime.isBefore(sunset))) {
- activate = mController.isActivated();
+ if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
+ ^ mLastActivatedTime.isBefore(sunset))) {
+ activate = mNightDisplayController.isActivated();
}
}
- if (mIsActivated == null || mIsActivated != activate) {
- mController.setActivated(activate);
+ if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+ mNightDisplayController.setActivated(activate);
}
}
@Override
+ public void onActivated(boolean activated) {
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
+ }
+
+ @Override
public void onStart() {
mTwilightManager.registerListener(this, mHandler);
+ mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
// Force an update to initialize state.
updateActivated(mTwilightManager.getLastTwilightState());
@@ -590,10 +694,7 @@
@Override
public void onStop() {
mTwilightManager.unregisterListener(this);
- }
-
- @Override
- public void onActivated(boolean activated) {
+ mLastActivatedTime = null;
}
@Override
@@ -624,6 +725,7 @@
}
private final class BinderService extends IColorDisplayManager.Stub {
+
@Override
public boolean isDeviceColorManaged() {
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0a1a9a2..b1ba05c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2144,6 +2144,14 @@
mContext.getPackageName());
}
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+ }
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 27cad1e..abbfc7b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -17,14 +17,9 @@
package com.android.server.display;
import android.content.Intent;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.util.Slog;
import java.io.PrintWriter;
-import java.lang.NumberFormatException;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
@@ -46,6 +41,10 @@
return setBrightness();
case "reset-brightness-configuration":
return resetBrightnessConfiguration();
+ case "ab-logging-enable":
+ return setAutoBrightnessLoggingEnabled(true);
+ case "ab-logging-disable":
+ return setAutoBrightnessLoggingEnabled(false);
default:
return handleDefaultCommands(cmd);
}
@@ -62,6 +61,10 @@
pw.println(" Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
pw.println(" reset-brightness-configuration");
pw.println(" Reset the brightness to its default configuration.");
+ pw.println(" ab-logging-enable");
+ pw.println(" Enable auto-brightness logging.");
+ pw.println(" ab-logging-disable");
+ pw.println(" Disable auto-brightness logging.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -89,4 +92,9 @@
mService.resetBrightnessConfiguration();
return 0;
}
+
+ private int setAutoBrightnessLoggingEnabled(boolean enabled) {
+ mService.setAutoBrightnessLoggingEnabled(enabled);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 249270b..c9ed9f7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -523,6 +523,9 @@
public void onSwitchUser(@UserIdInt int newUserId) {
handleSettingsChange(true /* userSwitch */);
mBrightnessTracker.onSwitchUser(newUserId);
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.onSwitchUser(newUserId);
+ }
}
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -1836,4 +1839,10 @@
mHandler.sendMessage(msg);
}
}
+
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.setLoggingEnabled(enabled);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 89cef62..9aec43b 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -16,13 +16,6 @@
package com.android.server.display;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
@@ -30,13 +23,20 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.Pair;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -50,12 +50,9 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
-import libcore.io.IoUtils;
-
/**
* Manages persistent state recorded by the display manager service as an XML file.
* Caller must acquire lock on the data store before accessing it.
@@ -110,14 +107,9 @@
private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
- private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
- private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
private static final String ATTR_USER_SERIAL = "user-serial";
private static final String ATTR_PACKAGE_NAME = "package-name";
private static final String ATTR_TIME_STAMP = "timestamp";
- private static final String ATTR_LUX = "lux";
- private static final String ATTR_NITS = "nits";
- private static final String ATTR_DESCRIPTION = "description";
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -646,7 +638,8 @@
}
try {
- BrightnessConfiguration config = loadConfigurationFromXml(parser);
+ BrightnessConfiguration config =
+ BrightnessConfiguration.loadFromXml(parser);
if (userSerial >= 0 && config != null) {
mConfigurations.put(userSerial, config);
if (timeStamp != -1) {
@@ -663,56 +656,6 @@
}
}
- private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- String description = null;
- Pair<float[], float[]> curve = null;
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
- description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
- curve = loadCurveFromXml(parser);
- }
- }
- if (curve == null) {
- return null;
- }
- final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
- curve.first, curve.second);
- builder.setDescription(description);
- return builder.build();
- }
-
- private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- List<Float> luxLevels = new ArrayList<>();
- List<Float> nitLevels = new ArrayList<>();
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
- luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
- nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
- }
- }
- final int N = luxLevels.size();
- float[] lux = new float[N];
- float[] nits = new float[N];
- for (int i = 0; i < N; i++) {
- lux[i] = luxLevels.get(i);
- nits[i] = nitLevels.get(i);
- }
- return Pair.create(lux, nits);
- }
-
- private static float loadFloat(String val) {
- try {
- return Float.parseFloat(val);
- } catch (NullPointerException | NumberFormatException e) {
- Slog.e(TAG, "Failed to parse float loading brightness config", e);
- return Float.NEGATIVE_INFINITY;
- }
- }
-
public void saveToXml(XmlSerializer serializer) throws IOException {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
@@ -728,27 +671,11 @@
if (timestamp != -1) {
serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
}
- saveConfigurationToXml(serializer, config);
+ config.saveToXml(serializer);
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
}
}
- private static void saveConfigurationToXml(XmlSerializer serializer,
- BrightnessConfiguration config) throws IOException {
- serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
- if (config.getDescription() != null) {
- serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
- }
- final Pair<float[], float[]> curve = config.getCurve();
- for (int i = 0; i < curve.first.length; i++) {
- serializer.startTag(null, TAG_BRIGHTNESS_POINT);
- serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
- serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
- serializer.endTag(null, TAG_BRIGHTNESS_POINT);
- }
- serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
- }
-
public void dump(final PrintWriter pw, final String prefix) {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c0d3fdf..f468c0bf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -81,8 +81,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import libcore.util.EmptyArray;
/**
@@ -94,6 +96,31 @@
private final Locale HONG_KONG = new Locale("zh", "HK");
private final Locale MACAU = new Locale("zh", "MO");
+ private static final Map<String, String> mTerminologyToBibliographicMap;
+ static {
+ mTerminologyToBibliographicMap = new HashMap<>();
+ // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
+ mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian
+ mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian
+ mTerminologyToBibliographicMap.put("eus", "baq"); // Basque
+ mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese
+ mTerminologyToBibliographicMap.put("ces", "cze"); // Czech
+ mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch
+ mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian
+ mTerminologyToBibliographicMap.put("deu", "ger"); // German
+ mTerminologyToBibliographicMap.put("ell", "gre"); // Greek
+ mTerminologyToBibliographicMap.put("fra", "fre"); // French
+ mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic
+ mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian
+ mTerminologyToBibliographicMap.put("mri", "mao"); // Maori
+ mTerminologyToBibliographicMap.put("msa", "may"); // Malay
+ mTerminologyToBibliographicMap.put("fas", "per"); // Persian
+ mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian
+ mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak
+ mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan
+ mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh
+ }
+
static final String PERMISSION = "android.permission.HDMI_CEC";
// The reason code to initiate initializeCec().
@@ -177,7 +204,18 @@
// Chinese used in Taiwan/Hong Kong/Macau.
return "chi";
} else {
- return locale.getISO3Language();
+ String language = locale.getISO3Language();
+
+ // locale.getISO3Language() returns terminology code and need to
+ // send it as bibliographic code instead since the Bibliographic
+ // codes of ISO/FDIS 639-2 shall be used.
+ // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
+ // But, as it depends on the locale, is not handled here.
+ if (mTerminologyToBibliographicMap.containsKey(language)) {
+ language = mTerminologyToBibliographicMap.get(language);
+ }
+
+ return language;
}
}
}
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index c0c4a6e..a3ebe24 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
@@ -86,13 +87,22 @@
protected final Object mLock = new Object();
/**
+ * Object used to define the name of the service component used to create
+ * {@link com.android.internal.infra.AbstractRemoteService} instances.
+ */
+ @Nullable
+ protected final ServiceNameResolver mServiceNameResolver;
+
+ /**
* Whether the service should log debug statements.
*/
+ //TODO(b/117779333): consider using constants for these guards
public boolean verbose = false;
/**
* Whether the service should log verbose statements.
*/
+ //TODO(b/117779333): consider using constants for these guards
public boolean debug = false;
/**
@@ -119,13 +129,24 @@
* Default constructor.
*
* @param context system context.
+ * @param serviceNameResolver resolver for
+ * {@link com.android.internal.infra.AbstractRemoteService} instances, or
+ * {@code null} when the service doesn't bind to remote services.
* @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
* disables the service.
*/
protected AbstractMasterSystemService(@NonNull Context context,
+ @Nullable ServiceNameResolver serviceNameResolver,
@Nullable String disallowProperty) {
super(context);
+ mServiceNameResolver = serviceNameResolver;
+ if (mServiceNameResolver != null) {
+ mServiceNameResolver
+ .setOnTemporaryServiceNameChangedCallback(
+ (u, s) -> updateCachedServiceLocked(u));
+
+ }
if (disallowProperty == null) {
mDisabledUsers = null;
} else {
@@ -199,6 +220,20 @@
}
/**
+ * Checks whether the service is allowed to bind to an instant-app.
+ *
+ * <p>Typically called by subclasses when creating {@link AbstractRemoteService} instances.
+ *
+ * <p><b>NOTE: </b>must not be called by {@code ShellCommand} as it does not check for
+ * permission.
+ */
+ public final boolean isBindInstantServiceAllowed() {
+ synchronized (mLock) {
+ return mAllowInstantService;
+ }
+ }
+
+ /**
* Sets whether the service is allowed to bind to an instant-app.
*
* <p>Typically called by {@code ShellCommand} during CTS tests.
@@ -294,6 +329,7 @@
*
* @return a new instance.
*/
+ @Nullable
protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
/**
@@ -421,11 +457,11 @@
/**
* Removes a cached service for a given user.
*
- * @return the removed service;
+ * @return the removed service.
*/
@GuardedBy("mLock")
@NonNull
- private S removeCachedServiceLocked(@UserIdInt int userId) {
+ protected final S removeCachedServiceLocked(@UserIdInt int userId) {
final S service = peekServiceForUserLocked(userId);
if (service != null) {
mServicesCache.delete(userId);
@@ -471,6 +507,10 @@
final int size = mServicesCache.size();
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
pw.print(" Verbose: "); pw.println(realVerbose);
+ if (mServiceNameResolver != null) {
+ pw.print(prefix); pw.print("Name resolver: ");
+ mServiceNameResolver.dumpShort(pw); pw.println();
+ }
pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
final String settingsProperty = getServiceSettingsProperty();
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 28a8094..556e489 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -55,8 +55,6 @@
protected final M mMaster;
- private final ServiceNameResolver mServiceNameResolver;
-
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
*/
@@ -72,13 +70,9 @@
@GuardedBy("mLock")
private ServiceInfo mServiceInfo;
- protected AbstractPerUserSystemService(@NonNull M master,
- @NonNull ServiceNameResolver serviceNamer, @NonNull Object lock,
+ protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
@UserIdInt int userId) {
mMaster = master;
- mServiceNameResolver = serviceNamer;
- mServiceNameResolver
- .setOnTemporaryServiceNameChangedCallback(() -> updateLocked(mDisabled));
mLock = lock;
mUserId = userId;
}
@@ -86,11 +80,21 @@
/**
* Creates a new {@link ServiceInfo} for the given service name.
*
+ * <p><b>MUST</b> be overridden by subclasses that bind to an
+ * {@link com.android.internal.infra.AbstractRemoteService}.
+ *
* @throws NameNotFoundException if the service does not exist.
* @throws SecurityException if the service does not have the proper permissions to be bound to.
+ * @throws UnsupportedOperationException if subclass binds to a remote service but does not
+ * overrides it.
+ *
+ * @return new {@link ServiceInfo},
*/
- protected abstract ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
- throws NameNotFoundException;
+ protected @NonNull ServiceInfo newServiceInfoLocked(
+ @SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException("not overridden");
+ }
/**
* Callback called when an app has been updated.
@@ -134,42 +138,54 @@
mSetupComplete = isSetupCompletedLocked();
mDisabled = disabled;
- ComponentName serviceComponent = null;
- ServiceInfo serviceInfo = null;
- final String componentName = getComponentNameLocked();
- if (!TextUtils.isEmpty(componentName)) {
- try {
- serviceComponent = ComponentName.unflattenFromString(componentName);
- serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
- 0, mUserId);
- if (serviceInfo == null) {
- Slog.e(mTag, "Bad service name: " + componentName);
- }
- } catch (RuntimeException | RemoteException e) {
- Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
- serviceInfo = null;
- }
- }
- try {
- if (serviceInfo != null) {
- mServiceInfo = newServiceInfo(serviceComponent);
- if (mMaster.debug) {
- Slog.d(mTag, "Set component for user " + mUserId + " as " + mServiceInfo);
- }
- } else {
- mServiceInfo = null;
- if (mMaster.debug) {
- Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
- }
- }
- } catch (Exception e) {
- Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
- mServiceInfo = null;
- }
+
+ updateServiceInfoLocked();
return wasEnabled != isEnabledLocked();
}
/**
+ * Updates the internal reference to the service info, and returns the service's component.
+ */
+ protected final ComponentName updateServiceInfoLocked() {
+ ComponentName serviceComponent = null;
+ if (mMaster.mServiceNameResolver != null) {
+ ServiceInfo serviceInfo = null;
+ final String componentName = getComponentNameLocked();
+ if (!TextUtils.isEmpty(componentName)) {
+ try {
+ serviceComponent = ComponentName.unflattenFromString(componentName);
+ serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ 0, mUserId);
+ if (serviceInfo == null) {
+ Slog.e(mTag, "Bad service name: " + componentName);
+ }
+ } catch (RuntimeException | RemoteException e) {
+ Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
+ serviceInfo = null;
+ }
+ }
+ try {
+ if (serviceInfo != null) {
+ mServiceInfo = newServiceInfoLocked(serviceComponent);
+ if (mMaster.debug) {
+ Slog.d(mTag, "Set component for user " + mUserId + " as "
+ + serviceComponent + " and info as " + mServiceInfo);
+ }
+ } else {
+ mServiceInfo = null;
+ if (mMaster.debug) {
+ Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
+ mServiceInfo = null;
+ }
+ }
+ return serviceComponent;
+ }
+
+ /**
* Gets the user associated with this service.
*/
public final @UserIdInt int getUserId() {
@@ -200,15 +216,15 @@
* Gets the current name of the service, which is either the default service or the
* {@link #setTemporaryServiceLocked(String, int) temporary one}.
*/
- protected final String getComponentNameLocked() {
- return mServiceNameResolver.getServiceNameLocked();
+ protected final @Nullable String getComponentNameLocked() {
+ return mMaster.mServiceNameResolver.getServiceName(mUserId);
}
/**
* Checks whether the current service for the user was temporarily set.
*/
public final boolean isTemporaryServiceSetLocked() {
- return mServiceNameResolver.isTemporaryLocked();
+ return mMaster.mServiceNameResolver.isTemporary(mUserId);
}
/**
@@ -219,14 +235,14 @@
* to the default component after this timeout expires).
*/
protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
- mServiceNameResolver.setTemporaryServiceLocked(componentName, durationMs);
+ mMaster.mServiceNameResolver.setTemporaryService(mUserId, componentName, durationMs);
}
/**
* Resets the temporary service implementation to the default component.
*/
protected final void resetTemporaryServiceLocked() {
- mServiceNameResolver.resetTemporaryServiceLocked();
+ mMaster.mServiceNameResolver.resetTemporaryService(mUserId);
}
/**
@@ -272,8 +288,16 @@
}
/**
+ * Removes the service from the master's cache.
+ */
+ protected final void removeSelfFromCacheLocked() {
+ mMaster.removeCachedServiceLocked(mUserId);
+ }
+
+ /**
* Whether the service should log debug statements.
*/
+ //TODO(b/117779333): consider using constants for these guards
public final boolean isDebug() {
return mMaster.debug;
}
@@ -281,6 +305,7 @@
/**
* Whether the service should log verbose statements.
*/
+ //TODO(b/117779333): consider using constants for these guards
public final boolean isVerbose() {
return mMaster.verbose;
}
@@ -313,13 +338,16 @@
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+ if (mMaster.mServiceNameResolver != null) {
+ pw.print(prefix); pw.print("Name resolver: ");
+ mMaster.mServiceNameResolver.dumpShort(pw, mUserId); pw.println();
+ }
pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
if (mServiceInfo != null) {
pw.print(prefix); pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
- pw.print(prefix); pw.print("Name resolver: "); mServiceNameResolver.dumpShortLocked(pw);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 27822d5..7027246 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -26,6 +26,7 @@
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -42,23 +43,22 @@
private static final String TAG = FrameworkResourcesServiceNameResolver.class.getSimpleName();
- /** Handler message to {@link #resetTemporaryServiceLocked()} */
+ /** Handler message to {@link #resetTemporaryService(int)} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
private final @NonNull Context mContext;
- private final @NonNull @UserIdInt int mUserId;
- private final @NonNull Object mLock;
+ private final @NonNull Object mLock = new Object();
private final @StringRes int mResourceId;
- private @Nullable Runnable mOnSetCallback;
+ private @Nullable NameResolverListener mOnSetCallback;
/**
- * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
+ * Map of temporary service name set by {@link #setTemporaryService(int, String, int)},
+ * keyed by {@code userId}.
*
* <p>Typically used by Shell command and/or CTS tests.
*/
@GuardedBy("mLock")
- @Nullable
- private String mTemporaryServiceName;
+ private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>();
/**
* When the temporary service will expire (and reset back to the default).
@@ -72,99 +72,123 @@
@GuardedBy("mLock")
private Handler mTemporaryHandler;
- public FrameworkResourcesServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
- @NonNull Object lock, @StringRes int resourceId) {
- mLock = lock;
+ public FrameworkResourcesServiceNameResolver(@NonNull Context context,
+ @StringRes int resourceId) {
mContext = context;
- mUserId = userId;
mResourceId = resourceId;
}
@Override
- public void setOnTemporaryServiceNameChangedCallback(@NonNull Runnable callback) {
- this.mOnSetCallback = callback;
- }
-
- @Override
- public String getDefaultServiceName() {
- final String name = mContext.getString(mResourceId);
- return TextUtils.isEmpty(name) ? null : name;
- }
-
- @Override
- public String getServiceNameLocked() {
- if (mTemporaryServiceName != null) {
- // Always log it, as it should only be used on CTS or during development
- Slog.w(TAG, "getComponentName(): using temporary name " + mTemporaryServiceName);
- return mTemporaryServiceName;
- } else {
- return getDefaultServiceName();
+ public void setOnTemporaryServiceNameChangedCallback(@NonNull NameResolverListener callback) {
+ synchronized (mLock) {
+ this.mOnSetCallback = callback;
}
}
@Override
- public boolean isTemporaryLocked() {
- return mTemporaryServiceName != null;
+ public String getDefaultServiceName(@UserIdInt int userId) {
+ synchronized (mLock) {
+ final String name = mContext.getString(mResourceId);
+ return TextUtils.isEmpty(name) ? null : name;
+ }
}
@Override
- public void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
- mTemporaryServiceName = componentName;
+ public String getServiceName(@UserIdInt int userId) {
+ synchronized (mLock) {
+ final String temporaryName = mTemporaryServiceNames.get(userId);
+ if (temporaryName != null) {
+ // Always log it, as it should only be used on CTS or during development
+ Slog.w(TAG, "getComponentName(): using temporary name " + temporaryName
+ + " for user " + userId);
+ return temporaryName;
+ } else {
+ return getDefaultServiceName(userId);
+ }
+ }
+ }
- if (mTemporaryHandler == null) {
- mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
- synchronized (mLock) {
- resetTemporaryServiceLocked();
+ @Override
+ public boolean isTemporary(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mTemporaryServiceNames.get(userId) != null;
+ }
+ }
+
+ @Override
+ public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+ int durationMs) {
+ synchronized (mLock) {
+ mTemporaryServiceNames.put(userId, componentName);
+
+ if (mTemporaryHandler == null) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+ synchronized (mLock) {
+ resetTemporaryService(userId);
+ }
+ } else {
+ Slog.wtf(TAG, "invalid handler msg: " + msg);
}
- } else {
- Slog.wtf(TAG, "invalid handler msg: " + msg);
}
- }
- };
- } else {
- removeResetTemporaryServiceMessageLocked();
+ };
+ } else {
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+ }
+ mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
+ mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+ notifyTemporaryServiceNameChangedLocked(userId, componentName);
}
- mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
- mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
- onServiceNameChanged();
}
@Override
- public void resetTemporaryServiceLocked() {
- Slog.i(TAG, "resetting temporary service from " + mTemporaryServiceName);
- mTemporaryServiceName = null;
- if (mTemporaryHandler != null) {
- removeResetTemporaryServiceMessageLocked();
- mTemporaryHandler = null;
+ public void resetTemporaryService(@UserIdInt int userId) {
+ synchronized (mLock) {
+ Slog.i(TAG, "resetting temporary service for user " + userId + " from "
+ + mTemporaryServiceNames.get(userId));
+ mTemporaryServiceNames.remove(userId);
+ if (mTemporaryHandler != null) {
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+ mTemporaryHandler = null;
+ }
+ notifyTemporaryServiceNameChangedLocked(userId, /* newTemporaryName= */ null);
}
- onServiceNameChanged();
+ }
+
+ @Override
+ public String toString() {
+ return "FrameworkResourcesServiceNamer: temps=" + mTemporaryServiceNames;
}
// TODO(b/117779333): support proto
@Override
- public void dumpShortLocked(@NonNull PrintWriter pw) {
- pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
- if (mTemporaryServiceName != null) {
- pw.print(", tmpName="); pw.print(mTemporaryServiceName);
- final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
- pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print(")");
- pw.print(", defaultName="); pw.println(getDefaultServiceName());
- } else {
- pw.print(", serviceName="); pw.println(getDefaultServiceName());
+ public void dumpShort(@NonNull PrintWriter pw) {
+ synchronized (mLock) {
+ pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
+ pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size());
}
}
- private void onServiceNameChanged() {
+ // TODO(b/117779333): support proto
+ @Override
+ public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
+ synchronized (mLock) {
+ final String temporaryName = mTemporaryServiceNames.get(userId);
+ if (temporaryName != null) {
+ pw.print("tmpName="); pw.print(temporaryName);
+ final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
+ pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print("), ");
+ }
+ pw.print("defaultName="); pw.println(getDefaultServiceName(userId));
+ }
+ }
+
+ private void notifyTemporaryServiceNameChangedLocked(@UserIdInt int userId,
+ @Nullable String newTemporaryName) {
if (mOnSetCallback != null) {
- mOnSetCallback.run();
+ mOnSetCallback.onNameResolved(userId, newTemporaryName);
}
}
-
- private void removeResetTemporaryServiceMessageLocked() {
- // NOTE: caller should already have checked it
- mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
- }
}
diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
index 7af8f60..bcaa918 100644
--- a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
@@ -31,27 +31,29 @@
public final class SecureSettingsServiceNameResolver implements ServiceNameResolver {
private final @NonNull Context mContext;
- private final @NonNull @UserIdInt int mUserId;
@NonNull
private final String mProperty;
- public SecureSettingsServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
- @NonNull String property) {
+ public SecureSettingsServiceNameResolver(@NonNull Context context, @NonNull String property) {
mContext = context;
- mUserId = userId;
mProperty = property;
}
@Override
- public String getDefaultServiceName() {
- return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, mUserId);
+ public String getDefaultServiceName(@UserIdInt int userId) {
+ return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, userId);
}
// TODO(b/117779333): support proto
@Override
- public void dumpShortLocked(@NonNull PrintWriter pw) {
+ public void dumpShort(@NonNull PrintWriter pw) {
pw.print("SecureSettingsServiceNamer: prop="); pw.print(mProperty);
- pw.print(", value="); pw.println(getDefaultServiceName());
+ }
+
+ // TODO(b/117779333): support proto
+ @Override
+ public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
+ pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
}
}
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index 205d40b..bc11ff3 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -17,6 +17,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+import com.android.internal.infra.AbstractRemoteService;
import java.io.PrintWriter;
@@ -29,78 +32,90 @@
public interface ServiceNameResolver {
/**
+ * Listener for name changes.
+ */
+ public interface NameResolverListener {
+
+ /**
+ * The name change callback.
+ */
+ void onNameResolved(@UserIdInt int userId, @Nullable String serviceName);
+ }
+
+ /**
* Sets a callback that is called after the service is
- * {@link #setTemporaryServiceLocked(String, int) set} or
- * {@link #resetTemporaryServiceLocked() reset}.
+ * {@link #setTemporaryService(int, String, int) set} or
+ * {@link #resetTemporaryService(int) reset}.
*
* <p>Typically called after the object is constructed.
*/
default void setOnTemporaryServiceNameChangedCallback(
- @SuppressWarnings("unused") @NonNull Runnable callback) {
+ @SuppressWarnings("unused") @NonNull NameResolverListener callback) {
// ignored by default
}
/**
- * Gets the default name of the service.
+ * Gets the default name of the service for the given user.
*
* <p>Typically implemented by reading a Settings property or framework resource.
*/
@Nullable
- String getDefaultServiceName();
+ String getDefaultServiceName(@UserIdInt int userId);
/**
- * Gets the current name of the service.
+ * Gets the current name of the service for the given user
*
- * <p>Must be called after acquiring this object's lock.
- *
- * @return either the temporary name (set by {@link #setTemporaryServiceLocked(String, int)}, or
- * the {@link #getDefaultServiceName() default name}.
+ * @return either the temporary name (set by
+ * {@link #setTemporaryService(int, String, int)}, or the
+ * {@link #getDefaultServiceName(int) default name}.
*/
@Nullable
- default String getServiceNameLocked() {
- return getDefaultServiceName();
+ default String getServiceName(@UserIdInt int userId) {
+ return getDefaultServiceName(userId);
}
/**
- * Checks whether the current service is temporary.
- *
- * <p>Must be called after acquiring this object's lock.
+ * Checks whether the current service is temporary for the given user.
*/
- default boolean isTemporaryLocked() {
+ default boolean isTemporary(@SuppressWarnings("unused") @UserIdInt int userId) {
return false;
}
/**
- * Temporarily sets the service implementation.
+ * Temporarily sets the service implementation for the given user.
*
- * <p>Must be called after acquiring this object's lock.
- *
+ * @param userId user handle
* @param componentName name of the new component
* @param durationMs how long the change will be valid (the service will be automatically reset
* to the default component after this timeout expires).
*
* @throws UnsupportedOperationException if not implemented.
*/
- default void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+ default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+ int durationMs) {
throw new UnsupportedOperationException("temporary user not supported");
}
/**
- * Resets the temporary service implementation to the default component.
+ * Resets the temporary service implementation to the default component for the given user.
*
- * <p>Must be called after acquiring this object's lock.
+ * @param userId user handle
*
* @throws UnsupportedOperationException if not implemented.
*/
- default void resetTemporaryServiceLocked() {
+ default void resetTemporaryService(@UserIdInt int userId) {
throw new UnsupportedOperationException("temporary user not supported");
}
/**
- * Dump this object in just one line (without calling {@code println}.
- *
- * <p>Must be called after acquiring this object's lock.
+ * Dumps the generic info in just one line (without calling {@code println}.
*/
// TODO(b/117779333): support proto
- void dumpShortLocked(@NonNull PrintWriter pw);
+ void dumpShort(@NonNull PrintWriter pw);
+
+ /**
+ * Dumps the user-specific info in just one line (without calling {@code println}.
+ */
+ // TODO(b/117779333): support proto
+ void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3552e66..fb6eaa0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -73,6 +73,7 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
@@ -3275,7 +3276,11 @@
final int packageNum = packageInfos.length;
for (int i = 0; i < packageNum; ++i) {
if (packageInfos[i].equals(imi.getPackageName())) {
- mFileManager.addInputMethodSubtypes(imi, subtypes);
+ if (subtypes.length > 0) {
+ mFileManager.addInputMethodSubtypes(imi, subtypes);
+ } else {
+ mFileManager.deleteAllInputMethodSubtypes(imi.getId());
+ }
final long ident = Binder.clearCallingIdentity();
try {
buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
@@ -4296,19 +4301,10 @@
? new File(Environment.getDataDirectory(), SYSTEM_PATH)
: Environment.getUserSystemDirectory(userId);
final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
- if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
- Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath());
- }
final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile, "input-subtypes");
- if (!subtypeFile.exists()) {
- // If "subtypes.xml" doesn't exist, create a blank file.
- writeAdditionalInputMethodSubtypes(
- mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
- } else {
- readAdditionalInputMethodSubtypes(
- mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
- }
+ readAdditionalInputMethodSubtypes(mAdditionalSubtypesMap,
+ mAdditionalInputMethodSubtypeFile);
}
private void deleteAllInputMethodSubtypes(String imiId) {
@@ -4348,6 +4344,25 @@
private static void writeAdditionalInputMethodSubtypes(
ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
ArrayMap<String, InputMethodInfo> methodMap) {
+ if (allSubtypes.isEmpty()) {
+ if (subtypesFile.exists()) {
+ subtypesFile.delete();
+ }
+ final File parentDir = subtypesFile.getBaseFile().getParentFile();
+ if (parentDir != null && FileUtils.listFilesOrEmpty(parentDir).length == 0) {
+ if (!parentDir.delete()) {
+ Slog.e(TAG, "Failed to delete the empty parent directory " + parentDir);
+ }
+ }
+ return;
+ }
+
+ final File parentDir = subtypesFile.getBaseFile().getParentFile();
+ if (!parentDir.exists() && !parentDir.mkdirs()) {
+ Slog.e(TAG, "Failed to create a parent directory " + parentDir);
+ return;
+ }
+
// Safety net for the case that this function is called before methodMap is set.
final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
FileOutputStream fos = null;
@@ -4404,6 +4419,10 @@
ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
if (allSubtypes == null || subtypesFile == null) return;
allSubtypes.clear();
+ if (!subtypesFile.exists()) {
+ // Not having the file means there is no additional subtype.
+ return;
+ }
try (final FileInputStream fis = subtypesFile.openRead()) {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, StandardCharsets.UTF_8.name());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 8e3f351..918dc07 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -38,11 +38,11 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SpellCheckerInfo;
-import android.view.textservice.TextServicesManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.StartInputFlags;
+import com.android.server.textservices.TextServicesManagerInternal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -641,7 +641,7 @@
}
// Only the current spell checker should be treated as an enabled one.
final SpellCheckerInfo currentSpellChecker =
- TextServicesManager.getInstance().getCurrentSpellChecker();
+ TextServicesManagerInternal.get().getCurrentSpellCheckerForUser(userId);
for (final String packageName : systemImesDisabledUntilUsed) {
if (DEBUG) {
Slog.d(TAG, "check " + packageName);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 29e1878..b6954fc 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -170,17 +170,13 @@
private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
private static final int GPS_CAPABILITY_GEOFENCING = 0x0000020;
- private static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
+ public static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
private static final int GPS_CAPABILITY_NAV_MESSAGES = 0x0000080;
// The AGPS SUPL mode
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
- // these need to match AGnssType enum in IAGnssCallback.hal
- private static final int AGPS_TYPE_SUPL = 1;
- private static final int AGPS_TYPE_C2K = 2;
-
// Handler messages
private static final int CHECK_LOCATION = 1;
private static final int ENABLE = 2;
@@ -642,6 +638,17 @@
Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
}
}
+
+ String emergencyExtensionSecondsString
+ = properties.getProperty("ES_EXTENSION_SEC", "0");
+ try {
+ int emergencyExtensionSeconds =
+ Integer.parseInt(emergencyExtensionSecondsString);
+ mNIHandler.setEmergencyExtensionSeconds(emergencyExtensionSeconds);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse ES_EXTENSION_SEC: "
+ + emergencyExtensionSecondsString);
+ }
}
private void loadPropertiesFromResource(Context context,
@@ -962,7 +969,8 @@
if (mSuplServerHost != null
&& mSuplServerPort > TCP_MIN_PORT
&& mSuplServerPort <= TCP_MAX_PORT) {
- native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+ native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
+ mSuplServerHost, mSuplServerPort);
}
}
@@ -1014,10 +1022,12 @@
// TODO: remove the following native calls if we can make sure they are redundant.
if (mSuplServerHost != null) {
- native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+ native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
+ mSuplServerHost, mSuplServerPort);
}
if (mC2KServerHost != null) {
- native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+ native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_C2K,
+ mC2KServerHost, mC2KServerPort);
}
mGnssMeasurementsProvider.onGpsEnabledChanged();
@@ -1564,8 +1574,8 @@
}
@NativeEntryPoint
- private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
- mNetworkConnectivityHandler.onReportAGpsStatus(type, status, ipaddr);
+ private void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
+ mNetworkConnectivityHandler.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
}
@NativeEntryPoint
@@ -1596,20 +1606,20 @@
@NativeEntryPoint
private void setEngineCapabilities(final int capabilities) {
// send to handler thread for fast native return, and in-order handling
- mHandler.post(() -> {
- mEngineCapabilities = capabilities;
+ mHandler.post(
+ () -> {
+ mEngineCapabilities = capabilities;
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
- mNtpTimeHelper.enablePeriodicTimeInjection();
- requestUtcTime();
- }
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+ mNtpTimeHelper.enablePeriodicTimeInjection();
+ requestUtcTime();
+ }
- mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_MEASUREMENTS));
- mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
- GPS_CAPABILITY_NAV_MESSAGES));
- restartRequests();
- });
+ mGnssMeasurementsProvider.onCapabilitiesUpdated(capabilities);
+ mGnssNavigationMessageProvider.onCapabilitiesUpdated(
+ hasCapability(GPS_CAPABILITY_NAV_MESSAGES));
+ restartRequests();
+ });
}
private void restartRequests() {
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 3e2ba87..77dee82 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.content.Context;
+import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementsEvent;
import android.location.IGnssMeasurementsListener;
import android.os.Handler;
@@ -27,14 +28,13 @@
import com.android.internal.annotations.VisibleForTesting;
/**
- * An base implementation for GPS measurements provider.
- * It abstracts out the responsibility of handling listeners, while still allowing technology
- * specific implementations to be built.
+ * An base implementation for GPS measurements provider. It abstracts out the responsibility of
+ * handling listeners, while still allowing technology specific implementations to be built.
*
* @hide
*/
-public abstract class GnssMeasurementsProvider extends
- RemoteListenerHelper<IGnssMeasurementsListener> {
+public abstract class GnssMeasurementsProvider
+ extends RemoteListenerHelper<IGnssMeasurementsListener> {
private static final String TAG = "GnssMeasurementsProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -42,19 +42,19 @@
private boolean mIsCollectionStarted;
private boolean mEnableFullTracking;
+ private int mGnssEngineCapabilities;
protected GnssMeasurementsProvider(Context context, Handler handler) {
this(context, handler, new GnssMeasurementProviderNative());
}
@VisibleForTesting
- GnssMeasurementsProvider(Context context, Handler handler,
- GnssMeasurementProviderNative aNative) {
+ GnssMeasurementsProvider(
+ Context context, Handler handler, GnssMeasurementProviderNative aNative) {
super(context, handler, TAG);
mNative = aNative;
}
- // TODO(b/37460011): Use this with death recovery logic.
void resumeIfStarted() {
if (DEBUG) {
Log.d(TAG, "resumeIfStarted");
@@ -87,6 +87,25 @@
}
}
+ /**
+ * Injects GNSS measurement corrections into the GNSS chipset.
+ *
+ * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+ * measurement corrections to be injected into the GNSS chipset.
+ */
+ public void injectGnssMeasurementCorrections(
+ GnssMeasurementCorrections measurementCorrections) {
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (!mNative.injectGnssMeasurementCorrections(measurementCorrections)) {
+ Log.e(TAG, "Failure in injecting GNSS corrections.");
+ }
+ }
+ });
+ }
+
@Override
protected void unregisterFromService() {
boolean stopped = mNative.stopMeasurementCollection();
@@ -96,20 +115,31 @@
}
public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
- foreach((IGnssMeasurementsListener listener, int uid, String packageName) -> {
- if (!hasPermission(uid, packageName)) {
- logPermissionDisabledEventNotReported(TAG, packageName, "GNSS measurements");
- return;
- }
- listener.onGnssMeasurementsReceived(event);
- });
+ foreach(
+ (IGnssMeasurementsListener listener, int uid, String packageName) -> {
+ if (!hasPermission(uid, packageName)) {
+ logPermissionDisabledEventNotReported(
+ TAG, packageName, "GNSS measurements");
+ return;
+ }
+ listener.onGnssMeasurementsReceived(event);
+ });
}
- public void onCapabilitiesUpdated(boolean isGnssMeasurementsSupported) {
+ /** Updates the framework about the capabilities of the GNSS chipset */
+ public void onCapabilitiesUpdated(int capabilities) {
+ mGnssEngineCapabilities = capabilities;
+ boolean isGnssMeasurementsSupported =
+ (capabilities & GnssLocationProvider.GPS_CAPABILITY_MEASUREMENTS) != 0;
setSupported(isGnssMeasurementsSupported);
updateResult();
}
+ /** Obtains the GNSS engine capabilities. */
+ public int getGnssCapabilities() {
+ return mGnssEngineCapabilities;
+ }
+
public void onGpsEnabledChanged() {
tryUpdateRegistrationWithService();
updateResult();
@@ -170,6 +200,11 @@
public boolean stopMeasurementCollection() {
return native_stop_measurement_collection();
}
+
+ public boolean injectGnssMeasurementCorrections(
+ GnssMeasurementCorrections measurementCorrections) {
+ return native_inject_gnss_measurement_corrections(measurementCorrections);
+ }
}
private static native boolean native_is_measurement_supported();
@@ -177,4 +212,7 @@
private static native boolean native_start_measurement_collection(boolean enableFullTracking);
private static native boolean native_stop_measurement_collection();
+
+ private static native boolean native_inject_gnss_measurement_corrections(
+ GnssMeasurementCorrections measurementCorrections);
}
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index 3903f2a..c38373f 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -68,6 +68,12 @@
private static final int AGNSS_NET_CAPABILITY_NOT_METERED = 1 << 0;
private static final int AGNSS_NET_CAPABILITY_NOT_ROAMING = 1 << 1;
+ // these need to match AGnssType enum in IAGnssCallback.hal
+ public static final int AGPS_TYPE_SUPL = 1;
+ public static final int AGPS_TYPE_C2K = 2;
+ private static final int AGPS_TYPE_EIMS = 3;
+ private static final int AGPS_TYPE_IMS = 4;
+
// Default time limit in milliseconds for the ConnectivityManager to find a suitable
// network with SUPL connectivity or report an error.
private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000;
@@ -86,6 +92,7 @@
private int mAGpsDataConnectionState;
private InetAddress mAGpsDataConnectionIpAddr;
+ private int mAGpsType;
private final Context mContext;
@@ -198,21 +205,11 @@
/**
* called from native code to update AGPS status
*/
- public void onReportAGpsStatus(int type, int status, byte[] ipaddr) {
- switch (status) {
+ public void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
+ switch (agpsStatus) {
case GPS_REQUEST_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
- Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(ipaddr));
- InetAddress connectionIpAddress = null;
- if (ipaddr != null) {
- try {
- connectionIpAddress = InetAddress.getByAddress(ipaddr);
- if (DEBUG) Log.d(TAG, "IP address converted to: " + connectionIpAddress);
- } catch (UnknownHostException e) {
- Log.e(TAG, "Bad IP Address: " + ipaddr, e);
- }
- }
- requestSuplConnection(connectionIpAddress);
+ requestSuplConnection(agpsType, suplIpAddr);
break;
case GPS_RELEASE_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
@@ -228,7 +225,7 @@
if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
break;
default:
- if (DEBUG) Log.d(TAG, "Received Unknown AGPS status: " + status);
+ if (DEBUG) Log.d(TAG, "Received Unknown AGPS status: " + agpsStatus);
}
}
@@ -308,8 +305,8 @@
};
}
- private void requestSuplConnection(InetAddress inetAddress) {
- postEvent(() -> handleRequestSuplConnection(inetAddress));
+ private void requestSuplConnection(int agpsType, byte[] suplIpAddr) {
+ postEvent(() -> handleRequestSuplConnection(agpsType, suplIpAddr));
}
private void suplConnectionAvailable(Network network) {
@@ -435,8 +432,15 @@
// exception in the following call to native_agps_data_conn_open
apn = "dummy-apn";
}
+
+ // Setting route to host is needed for GNSS HAL implementations earlier than
+ // @2.0::IAgnssCallback. The HAL @2.0::IAgnssCallback.agnssStatusCb() method does
+ // not require setting route to SUPL host and hence does not provide an IP address.
+ if (mAGpsDataConnectionIpAddr != null) {
+ setRouting();
+ }
+
int apnIpType = getApnIpType(apn);
- setRouting();
if (DEBUG) {
String message = String.format(
"native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
@@ -444,29 +448,40 @@
apnIpType);
Log.d(TAG, message);
}
- native_agps_data_conn_open(apn, apnIpType);
+ native_agps_data_conn_open(network.getNetworkHandle(), apn, apnIpType);
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
}
}
- private void handleRequestSuplConnection(InetAddress address) {
+ private void handleRequestSuplConnection(int agpsType, byte[] suplIpAddr) {
+ mAGpsDataConnectionIpAddr = null;
+ mAGpsType = agpsType;
+ if (suplIpAddr != null) {
+ if (VERBOSE) Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(suplIpAddr));
+ try {
+ mAGpsDataConnectionIpAddr = InetAddress.getByAddress(suplIpAddr);
+ if (DEBUG) Log.d(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Bad IP Address: " + suplIpAddr, e);
+ }
+ }
+
if (DEBUG) {
String message = String.format(
- "requestSuplConnection, state=%s, address=%s",
+ "requestSuplConnection, state=%s, agpsType=%s, address=%s",
agpsDataConnStateAsString(),
- address);
+ agpsTypeAsString(agpsType),
+ mAGpsDataConnectionIpAddr);
Log.d(TAG, message);
}
if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
return;
}
- mAGpsDataConnectionIpAddr = address;
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
- requestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- requestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ requestBuilder.addCapability(getNetworkCapability(mAGpsType));
NetworkRequest request = requestBuilder.build();
mConnMgr.requestNetwork(
request,
@@ -474,6 +489,20 @@
SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);
}
+ private int getNetworkCapability(int agpsType) {
+ switch (agpsType) {
+ case AGPS_TYPE_C2K:
+ case AGPS_TYPE_SUPL:
+ return NetworkCapabilities.NET_CAPABILITY_SUPL;
+ case AGPS_TYPE_EIMS:
+ return NetworkCapabilities.NET_CAPABILITY_EIMS;
+ case AGPS_TYPE_IMS:
+ return NetworkCapabilities.NET_CAPABILITY_IMS;
+ default:
+ throw new IllegalArgumentException("agpsType: " + agpsType);
+ }
+ }
+
private void handleReleaseSuplConnection(int agpsDataConnStatus) {
if (DEBUG) {
String message = String.format(
@@ -486,8 +515,8 @@
if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
return;
}
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback);
switch (agpsDataConnStatus) {
case GPS_AGPS_DATA_CONN_FAILED:
@@ -501,12 +530,9 @@
}
}
+ // TODO(25876485): Delete this method when all devices upgrade to HAL @2.0::IAGnssCallback
+ // interface which does not require setting route to host.
private void setRouting() {
- if (mAGpsDataConnectionIpAddr == null) {
- return;
- }
-
- // TODO(25876485): replace the use of this deprecated API
boolean result = mConnMgr.requestRouteToHostAddress(
ConnectivityManager.TYPE_MOBILE_SUPL,
mAGpsDataConnectionIpAddr);
@@ -564,6 +590,21 @@
}
}
+ private String agpsTypeAsString(int agpsType) {
+ switch (agpsType) {
+ case AGPS_TYPE_SUPL:
+ return "SUPL";
+ case AGPS_TYPE_C2K:
+ return "C2K";
+ case AGPS_TYPE_EIMS:
+ return "EIMS";
+ case AGPS_TYPE_IMS:
+ return "IMS";
+ default:
+ return "<Unknown>";
+ }
+ }
+
private int getApnIpType(String apn) {
ensureInHandlerThread();
if (apn == null) {
@@ -614,7 +655,7 @@
}
// AGPS support
- private native void native_agps_data_conn_open(String apn, int apnIpType);
+ private native void native_agps_data_conn_open(long networkHandle, String apn, int apnIpType);
private native void native_agps_data_conn_closed();
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 37d43fc..e69b2ec 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -43,7 +43,7 @@
protected static final int RESULT_UNKNOWN = 5;
protected static final int RESULT_NOT_ALLOWED = 6;
- private final Handler mHandler;
+ protected final Handler mHandler;
private final String mTag;
private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 405edd2..d961bad1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -842,25 +842,13 @@
// Report to usage stats that notification was made visible
if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
reportSeen(r);
-
- // If the newly visible notification has smart suggestions
- // then log that the user has seen them.
- if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0)
- && !r.hasSeenSmartReplies()) {
- r.setSeenSmartReplies(true);
- LogMaker logMaker = r.getLogMaker()
- .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
- .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
- r.getNumSmartRepliesAdded())
- .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT,
- r.getNumSmartActionsAdded())
- .addTaggedData(
- MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
- r.getSuggestionsGeneratedByAssistant());
- mMetricsLogger.write(logMaker);
- }
}
r.setVisibility(true, nv.rank, nv.count);
+ // hasBeenVisiblyExpanded must be called after updating the expansion state of
+ // the NotificationRecord to ensure the expansion state is up-to-date.
+ if (r.hasBeenVisiblyExpanded()) {
+ logSmartSuggestionsVisible(r);
+ }
maybeRecordInterruptionLocked(r);
nv.recycle();
}
@@ -884,6 +872,11 @@
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
r.stats.onExpansionChanged(userAction, expanded);
+ // hasBeenVisiblyExpanded must be called after updating the expansion state of
+ // the NotificationRecord to ensure the expansion state is up-to-date.
+ if (r.hasBeenVisiblyExpanded()) {
+ logSmartSuggestionsVisible(r);
+ }
final long now = System.currentTimeMillis();
if (userAction) {
MetricsLogger.action(r.getItemLogMaker()
@@ -961,6 +954,26 @@
}
};
+ @VisibleForTesting
+ void logSmartSuggestionsVisible(NotificationRecord r) {
+ // If the newly visible notification has smart suggestions
+ // then log that the user has seen them.
+ if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0)
+ && !r.hasSeenSmartReplies()) {
+ r.setSeenSmartReplies(true);
+ LogMaker logMaker = r.getLogMaker()
+ .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
+ r.getNumSmartRepliesAdded())
+ .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT,
+ r.getNumSmartActionsAdded())
+ .addTaggedData(
+ MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+ r.getSuggestionsGeneratedByAssistant());
+ mMetricsLogger.write(logMaker);
+ }
+ }
+
@GuardedBy("mNotificationLock")
private void clearSoundLocked() {
mSoundNotificationKey = null;
@@ -1939,9 +1952,11 @@
*/
@GuardedBy("mNotificationLock")
protected void reportSeen(NotificationRecord r) {
- mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- getRealUserId(r.sbn.getUserId()),
- UsageEvents.Event.NOTIFICATION_SEEN);
+ if (!r.isProxied()) {
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ getRealUserId(r.sbn.getUserId()),
+ UsageEvents.Event.NOTIFICATION_SEEN);
+ }
}
protected int calculateSuppressedVisualEffects(Policy incomingPolicy, Policy currPolicy,
@@ -2279,6 +2294,26 @@
}
@Override
+ public boolean areAppOverlaysAllowed(String pkg) {
+ return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid());
+ }
+
+ @Override
+ public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) {
+ checkCallerIsSystemOrSameApp(pkg);
+
+ return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid);
+ }
+
+ @Override
+ public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+ checkCallerIsSystem();
+
+ mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed);
+ handleSavePolicyFile();
+ }
+
+ @Override
public int getPackageImportance(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
@@ -2647,6 +2682,10 @@
* Note that since notification posting is done asynchronously, this will not return
* notifications that are in the process of being posted.
*
+ * From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as
+ * an app's notification delegate via
+ * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+ *
* @returns A list of all the package's notifications, in natural order.
*/
@Override
@@ -2689,16 +2728,18 @@
private StatusBarNotification sanitizeSbn(String pkg, int userId,
StatusBarNotification sbn) {
- if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
- // We could pass back a cloneLight() but clients might get confused and
- // try to send this thing back to notify() again, which would not work
- // very well.
- return new StatusBarNotification(
- sbn.getPackageName(),
- sbn.getOpPkg(),
- sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
- sbn.getNotification().clone(),
- sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+ if (sbn.getUserId() == userId) {
+ if (sbn.getPackageName().equals(pkg) || sbn.getOpPkg().equals(pkg)) {
+ // We could pass back a cloneLight() but clients might get confused and
+ // try to send this thing back to notify() again, which would not work
+ // very well.
+ return new StatusBarNotification(
+ sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+ sbn.getNotification().clone(),
+ sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+ }
}
return null;
}
@@ -4017,7 +4058,7 @@
final ArraySet<ComponentName> listeners =
mListenersDisablingEffects.valueAt(i);
for (int j = 0; j < listeners.size(); j++) {
- final ComponentName componentName = listeners.valueAt(i);
+ final ComponentName componentName = listeners.valueAt(j);
componentName.writeToProto(proto,
ListenersDisablingEffectsProto.LISTENER_COMPONENTS);
}
@@ -4162,8 +4203,8 @@
final int listenerSize = listeners.size();
for (int j = 0; j < listenerSize; j++) {
- if (i > 0) pw.print(',');
- final ComponentName listener = listeners.valueAt(i);
+ if (j > 0) pw.print(',');
+ final ComponentName listener = listeners.valueAt(j);
if (listener != null) {
pw.print(listener);
}
@@ -4403,7 +4444,7 @@
notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
}
- if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ if (notification.fullScreenIntent != null && ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
if (fullscreenIntentPermission != PERMISSION_GRANTED) {
@@ -4898,7 +4939,7 @@
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(r,
- NotificationListenerService.REASON_ERROR, null);
+ NotificationListenerService.REASON_ERROR, r.getStats());
mHandler.post(new Runnable() {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 89ec38d..e2c64ca 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -151,7 +151,6 @@
private int mSuppressedVisualEffects = 0;
private String mUserExplanation;
- private String mPeopleExplanation;
private boolean mPreChannelsNotification = true;
private Uri mSound;
private long[] mVibration;
@@ -1166,6 +1165,13 @@
mHasSeenSmartReplies = hasSeenSmartReplies;
}
+ /**
+ * Returns whether this notification has been visible and expanded at the same time.
+ */
+ public boolean hasBeenVisiblyExpanded() {
+ return stats.hasBeenVisiblyExpanded();
+ }
+
public void setSystemGeneratedSmartActions(
ArrayList<Notification.Action> systemGeneratedSmartActions) {
mSystemGeneratedSmartActions = systemGeneratedSmartActions;
@@ -1184,6 +1190,13 @@
}
/**
+ * Returns whether this notification was posted by a secondary app
+ */
+ public boolean isProxied() {
+ return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg());
+ }
+
+ /**
* @return all {@link Uri} that should have permission granted to whoever
* will be rendering it. This list has already been vetted to only
* include {@link Uri} that the enqueuing app can grant.
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index e40dad6..d630b9a 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -916,6 +916,13 @@
updateVisiblyExpandedStats();
}
+ /**
+ * Returns whether this notification has been visible and expanded at the same.
+ */
+ public boolean hasBeenVisiblyExpanded() {
+ return posttimeToFirstVisibleExpansionMs >= 0;
+ }
+
private void updateVisiblyExpandedStats() {
long elapsedNowMs = SystemClock.elapsedRealtime();
if (isExpanded && isVisible) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index eb46d53..7c0e0b0 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -80,6 +80,7 @@
private static final String ATT_NAME = "name";
private static final String ATT_UID = "uid";
private static final String ATT_ID = "id";
+ private static final String ATT_APP_OVERLAY = "overlay";
private static final String ATT_PRIORITY = "priority";
private static final String ATT_VISIBILITY = "visibility";
private static final String ATT_IMPORTANCE = "importance";
@@ -92,6 +93,7 @@
private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
private static final boolean DEFAULT_SHOW_BADGE = true;
+ private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
/**
* Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
* fields.
@@ -104,6 +106,7 @@
@IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
public @interface LockableAppFields {
int USER_LOCKED_IMPORTANCE = 0x00000001;
+ int USER_LOCKED_APP_OVERLAY = 0x00000002;
}
// pkg|uid => PackagePreferences
@@ -169,7 +172,9 @@
XmlUtils.readIntAttribute(
parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
XmlUtils.readBooleanAttribute(
- parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
+ parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
+ XmlUtils.readBooleanAttribute(
+ parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
r.importance = XmlUtils.readIntAttribute(
parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
r.priority = XmlUtils.readIntAttribute(
@@ -264,11 +269,12 @@
private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
return getOrCreatePackagePreferences(pkg, uid,
- DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
+ DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
+ DEFAULT_ALLOW_APP_OVERLAY);
}
private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
- int priority, int visibility, boolean showBadge) {
+ int priority, int visibility, boolean showBadge, boolean allowAppOverlay) {
final String key = packagePreferencesKey(pkg, uid);
synchronized (mPackagePreferences) {
PackagePreferences
@@ -282,6 +288,7 @@
r.priority = priority;
r.visibility = visibility;
r.showBadge = showBadge;
+ r.appOverlay = allowAppOverlay;
try {
createDefaultChannelIfNeeded(r);
@@ -382,7 +389,8 @@
|| r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
|| r.channels.size() > 0
|| r.groups.size() > 0
- || r.delegate != null;
+ || r.delegate != null
+ || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY;
if (hasNonDefaultSettings) {
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
@@ -395,6 +403,9 @@
if (r.visibility != DEFAULT_VISIBILITY) {
out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
}
+ if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) {
+ out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay));
+ }
out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
Integer.toString(r.lockedAppFields));
@@ -439,6 +450,20 @@
out.endTag(null, TAG_RANKING);
}
+ public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+ PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
+ p.appOverlay = allowed;
+ p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY;
+ }
+
+ public boolean areAppOverlaysAllowed(String pkg, int uid) {
+ return getOrCreatePackagePreferences(pkg, uid).appOverlay;
+ }
+
+ public int getAppLockedFields(String pkg, int uid) {
+ return getOrCreatePackagePreferences(pkg, uid).lockedAppFields;
+ }
+
/**
* Gets importance.
*/
@@ -512,7 +537,6 @@
// apps can't update the blocked status or app overlay permission
if (fromTargetApp) {
group.setBlocked(oldGroup.isBlocked());
- group.setAllowAppOverlay(oldGroup.canOverlayApps());
group.unlockFields(group.getUserLockedFields());
group.lockFields(oldGroup.getUserLockedFields());
} else {
@@ -521,9 +545,6 @@
group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
updateChannelsBypassingDnd(mContext.getUserId());
}
- if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
- group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
- }
}
}
r.groups.put(group.getId(), group);
@@ -1581,6 +1602,7 @@
int priority = DEFAULT_PRIORITY;
int visibility = DEFAULT_VISIBILITY;
boolean showBadge = DEFAULT_SHOW_BADGE;
+ boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY;
int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
Delegate delegate = null;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f01d343..e5b1878 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -558,9 +558,7 @@
rule.conditionId = automaticZenRule.getConditionId();
rule.enabled = automaticZenRule.isEnabled();
rule.modified = automaticZenRule.isModified();
- if (automaticZenRule.getZenPolicy() != null) {
- rule.zenPolicy = automaticZenRule.getZenPolicy();
- }
+ rule.zenPolicy = automaticZenRule.getZenPolicy();
rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 7ae2271..3a7919a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -120,7 +120,7 @@
android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
-1, true);
if (permissionFlag != PackageManager.PERMISSION_GRANTED
- || !mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)) {
+ || !isSameProfileGroup(callerUserId, userId)) {
throw new SecurityException("Attempt to launch activity without required "
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
+ " or target user is not in the same profile group.");
@@ -209,6 +209,15 @@
}
}
+ private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId);
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Verify that the given calling package is belong to the calling UID.
*/
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 093b85e..f9e31ae 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -402,12 +402,17 @@
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
- // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
- // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
- // in isolation (and avoid to extract/verify the main apk if it's in the class path).
- // Note this trades correctness for performance since the resulting slow down is
- // unacceptable in some cases until b/64530081 is fixed.
- String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+ String classLoaderContext;
+ if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
+ // If we have an unknown (not yet set), or a variable class loader chain, compile
+ // without a context and mark the oat file with SKIP_SHARED_LIBRARY_CHECK. Note that
+ // this might lead to a incorrect compilation.
+ // TODO(calin): We should just extract in this case.
+ classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+ } else {
+ classLoaderContext = dexUseInfo.getClassLoaderContext();
+ }
+
int reason = options.getCompilationReason();
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 206a88b..35ffe8d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -250,6 +250,15 @@
@GuardedBy("mLock")
private int mParentSessionId;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionApplied;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionReady;
+ @GuardedBy("mLock")
+ private boolean mStagedSessionFailed;
+ @GuardedBy("mLock")
+ private int mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+
/**
* Path to the validated base APK for this session, which may point at an
* APK inside the session (when the session defines the base), or it may
@@ -470,6 +479,10 @@
if (info.childSessionIds == null) {
info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
}
+ info.isSessionApplied = mStagedSessionApplied;
+ info.isSessionReady = mStagedSessionReady;
+ info.isSessionFailed = mStagedSessionFailed;
+ info.setStagedSessionErrorCode(mStagedSessionErrorCode);
}
return info;
}
@@ -1051,6 +1064,8 @@
}
if (isStaged()) {
// STOPSHIP: implement staged sessions
+ mStagedSessionReady = true;
+ mPm.sendSessionUpdatedBroadcast(generateInfo(), userId);
dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
return;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 28fb01d..0bde80a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -325,7 +325,6 @@
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
-import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -888,7 +887,7 @@
volatile boolean mSystemReady;
volatile boolean mSafeMode;
volatile boolean mHasSystemUidErrors;
- private volatile boolean mWebInstantAppsDisabled;
+ private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray();
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -2225,9 +2224,7 @@
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
- addSharedLibraryLPw(entry.filename, null, null, name,
- SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
- PLATFORM_PACKAGE_NAME, 0);
+ addBuiltInSharedLibraryLocked(entry.filename, name);
}
// Now that we have added all the libraries, iterate again to add dependency
@@ -5570,12 +5567,18 @@
* were updated, return true.
*/
private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
- final VersionInfo ver = getSettingsVersionForPackage(scannedPkg);
+ return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
+ }
+
+ private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
}
private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
- final VersionInfo ver = getSettingsVersionForPackage(scannedPkg);
+ return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
+ }
+
+ private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
}
@@ -5904,8 +5907,8 @@
/**
* Returns whether or not instant apps have been disabled remotely.
*/
- private boolean areWebInstantAppsDisabled() {
- return mWebInstantAppsDisabled;
+ private boolean areWebInstantAppsDisabled(int userId) {
+ return mWebInstantAppsDisabled.get(userId);
}
private boolean isInstantAppResolutionAllowed(
@@ -5936,7 +5939,7 @@
} else {
if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
return false;
- } else if (areWebInstantAppsDisabled()) {
+ } else if (areWebInstantAppsDisabled(userId)) {
return false;
}
}
@@ -6816,7 +6819,7 @@
private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
- final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled();
+ final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
// remove locally resolved instant app web results when disabled
@@ -8339,11 +8342,12 @@
// directory and not the APK file.
final long lastModifiedTime = mIsPreNMR1Upgrade
? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
+ final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg);
if (ps != null && !forceCollect
&& ps.codePathString.equals(pkg.codePath)
&& ps.timeStamp == lastModifiedTime
- && !isCompatSignatureUpdateNeeded(pkg)
- && !isRecoverSignatureUpdateNeeded(pkg)) {
+ && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+ && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
if (ps.signatures.mSigningDetails.signatures != null
&& ps.signatures.mSigningDetails.signatures.length != 0
&& ps.signatures.mSigningDetails.signatureSchemeVersion
@@ -8744,8 +8748,20 @@
if (scanResult.success) {
synchronized (mPackages) {
try {
+ final String pkgName = scanResult.pkgSetting.name;
+ final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
+ new ReconcileRequest(
+ Collections.singletonMap(pkgName, scanResult),
+ mSharedLibraries,
+ mPackages,
+ Collections.singletonMap(
+ pkgName, getSettingsVersionForPackage(pkg)),
+ Collections.singletonMap(pkgName,
+ getSharedLibLatestVersionSetting(scanResult))),
+ mSettings.mKeySetManagerService);
prepareScanResultLocked(scanResult);
- commitScanResultLocked(scanResult);
+ commitReconciledScanResultLocked(
+ reconcileResult.get(pkgName));
} catch (PackageManagerException e) {
unprepareScanResultLocked(scanResult);
throw e;
@@ -9352,8 +9368,26 @@
}
}
- private @Nullable SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
- LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
+ @Nullable
+ private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
+ return getSharedLibraryInfo(name, version, mSharedLibraries, null);
+ }
+
+ @Nullable
+ private static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
+ Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) {
+ if (newLibraries != null) {
+ final LongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
+ SharedLibraryInfo info = null;
+ if (versionedLib != null) {
+ info = versionedLib.get(version);
+ }
+ if (info != null) {
+ return info;
+ }
+ }
+ final LongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
if (versionedLib == null) {
return null;
}
@@ -9380,6 +9414,21 @@
return null;
}
+
+ @Nullable
+ private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
+ PackageSetting sharedLibPackage = null;
+ synchronized (mPackages) {
+ final SharedLibraryInfo latestSharedLibraVersionLPr =
+ getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg);
+ if (latestSharedLibraVersionLPr != null) {
+ sharedLibPackage = mSettings.getPackageLPr(
+ latestSharedLibraVersionLPr.getPackageName());
+ }
+ }
+ return sharedLibPackage;
+ }
+
public void shutdown() {
mPackageUsage.writeNow(mPackages);
mCompilerStats.writeNow();
@@ -9681,35 +9730,50 @@
@GuardedBy("mPackages")
private void updateSharedLibrariesLPr(PackageParser.Package pkg,
PackageParser.Package changingLib) throws PackageManagerException {
- if (pkg == null) {
- return;
- }
+ final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
+ collectSharedLibraryInfos(pkg, Collections.unmodifiableMap(mPackages),
+ mSharedLibraries, null);
+ executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos);
+ }
- // If the package provides libraries, clear their old dependencies.
- // This method will set them up again.
- applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
- definingLibrary.clearDependencies();
- });
+ private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(PackageParser.Package pkg,
+ Map<String, PackageParser.Package> availablePackages,
+ @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
+ throws PackageManagerException {
+ if (pkg == null) {
+ return null;
+ }
// The collection used here must maintain the order of addition (so
// that libraries are searched in the correct order) and must have no
// duplicates.
ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
if (pkg.usesLibraries != null) {
- usesLibraryInfos = addSharedLibrariesLPw(pkg.usesLibraries,
- null, null, pkg.packageName, true,
- pkg.applicationInfo.targetSdkVersion, null);
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null,
+ pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null,
+ availablePackages, existingLibraries, newLibraries);
}
if (pkg.usesStaticLibraries != null) {
- usesLibraryInfos = addSharedLibrariesLPw(pkg.usesStaticLibraries,
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries,
pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
- pkg.packageName, true,
- pkg.applicationInfo.targetSdkVersion, usesLibraryInfos);
+ pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos,
+ availablePackages, existingLibraries, newLibraries);
}
if (pkg.usesOptionalLibraries != null) {
- usesLibraryInfos = addSharedLibrariesLPw(pkg.usesOptionalLibraries,
- null, null, pkg.packageName, false,
- pkg.applicationInfo.targetSdkVersion, usesLibraryInfos);
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries,
+ null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion,
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
+ return usesLibraryInfos;
+ }
+
+ private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg,
+ PackageParser.Package changingLib, ArrayList<SharedLibraryInfo> usesLibraryInfos) {
+ // If the package provides libraries, clear their old dependencies.
+ // This method will set them up again.
+ applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
+ definingLibrary.clearDependencies();
+ });
if (usesLibraryInfos != null) {
pkg.usesLibraryInfos = usesLibraryInfos;
// Use LinkedHashSet to preserve the order of files added to
@@ -9726,18 +9790,22 @@
}
@GuardedBy("mPackages")
- private ArrayList<SharedLibraryInfo> addSharedLibrariesLPw(
+ private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
@NonNull String packageName, boolean required, int targetSdk,
- @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries)
+ @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+ @NonNull final Map<String, PackageParser.Package> availablePackages,
+ @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
throws PackageManagerException {
final int libCount = requestedLibraries.size();
for (int i = 0; i < libCount; i++) {
final String libName = requestedLibraries.get(i);
final long libVersion = requiredVersions != null ? requiredVersions[i]
: SharedLibraryInfo.VERSION_UNDEFINED;
- final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(libName, libVersion);
+ final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(libName, libVersion,
+ existingLibraries, newLibraries);
if (libraryInfo == null) {
if (required) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
@@ -9757,7 +9825,8 @@
+ libraryInfo.getLongVersion() + "; failing!");
}
- PackageParser.Package libPkg = mPackages.get(libraryInfo.getPackageName());
+ PackageParser.Package libPkg =
+ availablePackages.get(libraryInfo.getPackageName());
if (libPkg == null) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires unavailable static shared"
@@ -9930,15 +9999,24 @@
@Nullable public final PackageSetting pkgSetting;
/** ABI code paths that have changed in the package scan */
@Nullable public final List<String> changedAbiCodePath;
+
+ public final SharedLibraryInfo staticSharedLibraryInfo;
+
+ public final List<SharedLibraryInfo> dynamicSharedLibraryInfos;
+
public ScanResult(
ScanRequest request, boolean success,
@Nullable PackageSetting pkgSetting,
- @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied) {
+ @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
+ SharedLibraryInfo staticSharedLibraryInfo,
+ List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
this.request = request;
this.success = success;
this.pkgSetting = pkgSetting;
this.changedAbiCodePath = changedAbiCodePath;
this.existingSettingCopied = existingSettingCopied;
+ this.staticSharedLibraryInfo = staticSharedLibraryInfo;
+ this.dynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
}
}
@@ -10136,29 +10214,6 @@
}
- private void commitSuccessfulScanResults(@NonNull List<ScanResult> results)
- throws PackageManagerException {
- synchronized(mPackages) {
- for (ScanResult result : results) {
- // failures should have been caught earlier, but in case it wasn't,
- // let's double check
- if (!result.success) {
- throw new PackageManagerException(
- "Scan failed for " + result.request.pkg.packageName);
- }
- }
- for (ScanResult result : results) {
- try {
- prepareScanResultLocked(result);
- commitScanResultLocked(result);
- } catch (PackageManagerException e) {
- unprepareScanResultLocked(result);
- throw e;
- }
- }
- }
- }
-
/** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
private void prepareScanResultLocked(@NonNull ScanResult result)
throws PackageManagerException {
@@ -10191,7 +10246,8 @@
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy({"mPackages", "mInstallLock"})
- private void commitScanResultLocked(@NonNull ScanResult result) throws PackageManagerException {
+ private void commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg) {
+ final ScanResult result = reconciledPkg.scanResult;
final ScanRequest request = result.request;
final PackageParser.Package pkg = request.pkg;
final PackageParser.Package oldPkg = request.oldPkg;
@@ -10199,7 +10255,6 @@
final @ScanFlags int scanFlags = request.scanFlags;
final PackageSetting oldPkgSetting = request.oldPkgSetting;
final PackageSetting originalPkgSetting = request.originalPkgSetting;
- final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
final UserHandle user = request.user;
final String realPkgName = request.realPkgName;
final List<String> changedAbiCodePath = result.changedAbiCodePath;
@@ -10235,130 +10290,19 @@
mTransferedPackages.add(pkg.packageName);
}
- // THROWS: when requested libraries that can't be found. it only changes
- // the state of the passed in pkg object, so, move to the top of the method
- // and allow it to abort
- if ((scanFlags & SCAN_BOOTING) == 0
- && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- // Check all shared libraries and map to their actual file path.
- // We only do this here for apps not on a system dir, because those
- // are the only ones that can fail an install due to this. We
- // will take care of the system apps by updating all of their
- // library paths after the scan is done. Also during the initial
- // scan don't update any libs as we do this wholesale after all
- // apps are scanned to avoid dependency based scanning.
- updateSharedLibrariesLPr(pkg, null);
- }
-
- // All versions of a static shared library are referenced with the same
- // package name. Internally, we use a synthetic package name to allow
- // multiple versions of the same shared library to be installed. So,
- // we need to generate the synthetic package name of the latest shared
- // library in order to compare signatures.
- PackageSetting signatureCheckPs = pkgSetting;
- if (pkg.applicationInfo.isStaticSharedLibrary()) {
- SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(pkg);
- if (libraryInfo != null) {
- signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());
- }
+ if (reconciledPkg.collectedSharedLibraryInfos != null) {
+ executeSharedLibrariesUpdateLPr(pkg, null, reconciledPkg.collectedSharedLibraryInfos);
}
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
- if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
- } else {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + pkg.packageName + " upgrade keys do not match the "
- + "previously installed version");
- } else {
- pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
- String msg = "System package " + pkg.packageName
- + " signature changed; retaining data.";
- reportSettingsProblem(Log.WARN, msg);
- }
- }
- } else {
- try {
- final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
- final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
- final boolean compatMatch = verifySignatures(signatureCheckPs, disabledPkgSetting,
- pkg.mSigningDetails, compareCompat, compareRecover);
- // The new KeySets will be re-added later in the scanning process.
- if (compatMatch) {
- synchronized (mPackages) {
- ksms.removeAppKeySetDataLPw(pkg.packageName);
- }
- }
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
-
-
- // if this is is a sharedUser, check to see if the new package is signed by a newer
- // signing certificate than the existing one, and if so, copy over the new details
- if (signatureCheckPs.sharedUser != null) {
- if (pkg.mSigningDetails.hasAncestor(
- signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
- signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
- }
- if (signatureCheckPs.sharedUser.signaturesChanged == null) {
- signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
- }
- }
- } catch (PackageManagerException e) {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- throw e;
- }
- // The signature has changed, but this package is in the system
- // image... let's recover!
- pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
-
- // If the system app is part of a shared user we allow that shared user to change
- // signatures as well as part of an OTA. We still need to verify that the signatures
- // are consistent within the shared user for a given boot, so only allow updating
- // the signatures on the first package scanned for the shared user (i.e. if the
- // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
- if (signatureCheckPs.sharedUser != null) {
- if (signatureCheckPs.sharedUser.signaturesChanged != null &&
- compareSignatures(
- signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
- pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
- if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
- // Mismatched signatures is an error and silently skipping system
- // packages will likely break the device in unforeseen ways. However,
- // we allow the device to boot anyway because, prior to P, vendors were
- // not expecting the platform to crash in this situation.
- throw new PackageManagerException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Signature mismatch for shared user: " + pkgSetting.sharedUser);
- } else {
- // Treat mismatched signatures on system packages using a shared UID as
- // fatal for the system overall, rather than just failing to install
- // whichever package happened to be scanned later.
- throw new IllegalStateException("Signature mismatch on system package "
- + pkg.packageName + " for shared user " + pkgSetting.sharedUser);
- }
- }
-
- signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
- signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
- }
- // File a report about this.
- String msg = "System package " + pkg.packageName
- + " signature changed; retaining data.";
- reportSettingsProblem(Log.WARN, msg);
- } catch (IllegalArgumentException e) {
- // should never happen: certs matched when checking, but not when comparing
- // old to new for sharedUser
- throw new RuntimeException(
- "Signing certificates comparison made on incomparable signing details"
- + " but somehow passed verifySignatures!", e);
- }
+ if (reconciledPkg.removeAppKeySetData) {
+ ksms.removeAppKeySetDataLPw(pkg.packageName);
}
+ if (reconciledPkg.sharedUserSignaturesChanged) {
+ pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
+ pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.signingDetails;
+ }
+ pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
// This package wants to adopt ownership of permissions from
@@ -10396,8 +10340,8 @@
} else {
final int userId = user == null ? 0 : user.getIdentifier();
// Modify state for the given package setting
- commitPackageSettings(pkg, oldPkg, pkgSetting, user, scanFlags,
- (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+ commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
+ (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
if (pkgSetting.getInstantApp(userId)) {
mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
}
@@ -10568,11 +10512,10 @@
UserManagerService.getInstance(), usesStaticLibraries,
pkg.usesStaticLibrariesVersions);
} else {
- if (!createNewPackage) {
- // make a deep copy to avoid modifying any existing system state.
- pkgSetting = new PackageSetting(pkgSetting);
- pkgSetting.pkg = pkg;
- }
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.pkg = pkg;
+
// REMOVE SharedUserSetting from method; update in a separate call.
//
// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
@@ -10791,8 +10734,21 @@
pkgSetting.volumeUuid = volumeUuid;
}
+ SharedLibraryInfo staticSharedLibraryInfo = null;
+ if (!TextUtils.isEmpty(pkg.staticSharedLibName)) {
+ staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(pkg);
+ }
+ List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
+ if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
+ dynamicSharedLibraryInfos = new ArrayList<>(pkg.libraryNames.size());
+ for (String name : pkg.libraryNames) {
+ dynamicSharedLibraryInfos.add(SharedLibraryInfo.createForDynamic(pkg, name));
+ }
+ }
+
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
- !createNewPackage /* existingSettingCopied */);
+ !createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,
+ dynamicSharedLibraryInfos);
}
/**
@@ -11302,24 +11258,48 @@
}
}
- private boolean addSharedLibraryLPw(String path, String apk, List<String> codePaths,
- String name, long version, int type, String declaringPackageName,
- long declaringVersionCode) {
+ @GuardedBy("mPackages")
+ private boolean addBuiltInSharedLibraryLocked(String path, String name) {
+ if (nonStaticSharedLibExistsLocked(name)) {
+ return false;
+ }
+
+ SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
+ (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
+ new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
+ null, null);
+
+ commitSharedLibraryInfoLocked(libraryInfo);
+ return true;
+ }
+
+ @GuardedBy("mPackages")
+ private boolean nonStaticSharedLibExistsLocked(String name) {
+ return sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED, mSharedLibraries);
+ }
+
+ private static boolean sharedLibExists(final String name, final long version,
+ Map<String, LongSparseArray<SharedLibraryInfo>> librarySource) {
+ LongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
+ if (versionedLib != null && versionedLib.indexOfKey(version) >= 0) {
+ return true;
+ }
+ return false;
+ }
+
+ @GuardedBy("mPackages")
+ private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
+ final String name = libraryInfo.getName();
LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
if (versionedLib == null) {
versionedLib = new LongSparseArray<>();
mSharedLibraries.put(name, versionedLib);
- if (type == SharedLibraryInfo.TYPE_STATIC) {
- mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
- }
- } else if (versionedLib.indexOfKey(version) >= 0) {
- return false;
}
- SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, codePaths, name,
- version, type, new VersionedPackage(declaringPackageName, declaringVersionCode),
- null, null);
- versionedLib.put(version, libraryInfo);
- return true;
+ final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName();
+ if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
+ mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
+ }
+ versionedLib.put(libraryInfo.getLongVersion(), libraryInfo);
}
private boolean removeSharedLibraryLPw(String name, long version) {
@@ -11348,8 +11328,8 @@
* be available for query, resolution, etc...
*/
private void commitPackageSettings(PackageParser.Package pkg,
- @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
- final @ScanFlags int scanFlags, boolean chatty) {
+ @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting,
+ final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
final String pkgName = pkg.packageName;
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
@@ -11396,90 +11376,23 @@
ArrayList<PackageParser.Package> clientLibPkgs = null;
// writer
synchronized (mPackages) {
- boolean hasStaticSharedLibs = false;
-
- // Any app can add new static shared libraries
- if (pkg.staticSharedLibName != null) {
- // Static shared libs don't allow renaming as they have synthetic package
- // names to allow install of multiple versions, so use name from manifest.
- if (addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
- pkg.staticSharedLibName,
- pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
- pkg.manifestPackageName, pkg.getLongVersionCode())) {
- hasStaticSharedLibs = true;
- // Shared libraries for the package need to be updated.
- try {
- updateSharedLibrariesLPr(pkg, null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
- }
- } else {
- Slog.w(TAG, "Package " + pkg.packageName + " library "
- + pkg.staticSharedLibName + " already exists; skipping");
+ if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
+ for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
+ commitSharedLibraryInfoLocked(info);
}
- // Static shared libs cannot be updated once installed since they
- // use synthetic package name which includes the version code, so
- // not need to update other packages's shared lib dependencies.
+ try {
+ // Shared libraries for the package need to be updated.
+ updateSharedLibrariesLPr(pkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+ }
}
- if (!hasStaticSharedLibs
- && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- // Only system apps can add new dynamic shared libraries.
- if (pkg.libraryNames != null) {
- for (int i = 0; i < pkg.libraryNames.size(); i++) {
- String name = pkg.libraryNames.get(i);
- boolean allowed = false;
- if (pkg.isUpdatedSystemApp()) {
- // New library entries can only be added through the
- // system image. This is important to get rid of a lot
- // of nasty edge cases: for example if we allowed a non-
- // system update of the app to add a library, then uninstalling
- // the update would make the library go away, and assumptions
- // we made such as through app install filtering would now
- // have allowed apps on the device which aren't compatible
- // with it. Better to just have the restriction here, be
- // conservative, and create many fewer cases that can negatively
- // impact the user experience.
- final PackageSetting sysPs = mSettings
- .getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
- for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
- if (name.equals(sysPs.pkg.libraryNames.get(j))) {
- allowed = true;
- break;
- }
- }
- }
- } else {
- allowed = true;
- }
- if (allowed) {
- if (!addSharedLibraryLPw(null, pkg.packageName, pkg.getAllCodePaths(),
- name, SharedLibraryInfo.VERSION_UNDEFINED,
- SharedLibraryInfo.TYPE_DYNAMIC,
- pkg.packageName, pkg.getLongVersionCode())) {
- Slog.w(TAG, "Package " + pkg.packageName + " library "
- + name + " already exists; skipping");
- }
- // Shared libraries for the package need to be updated.
- try {
- updateSharedLibrariesLPr(pkg, null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
- }
- } else {
- Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
- + name + " that is not declared on system image; skipping");
- }
- }
-
- if ((scanFlags & SCAN_BOOTING) == 0) {
- // If we are not booting, we need to update any applications
- // that are clients of our shared library. If we are booting,
- // this will all be done once the scan is complete.
- clientLibPkgs = updateAllSharedLibrariesLPw(pkg);
- }
- }
+ if (reconciledPkg.hasDynamicSharedLibraries() && (scanFlags & SCAN_BOOTING) == 0) {
+ // If we are not booting, we need to update any applications
+ // that are clients of our shared library. If we are booting,
+ // this will all be done once the scan is complete.
+ clientLibPkgs = updateAllSharedLibrariesLPw(pkg);
}
}
@@ -15235,20 +15148,40 @@
private static class ReconcileRequest {
public final Map<String, ScanResult> scannedPackages;
- // TODO: Remove install-specific details from reconcile request; make them generic types
- // that can be used for scanDir for example.
+ public final Map<String, PackageParser.Package> allPackages;
+ public final Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource;
public final Map<String, InstallArgs> installArgs;
public final Map<String, PackageInstalledInfo> installResults;
public final Map<String, PrepareResult> preparedPackages;
+ public final Map<String, VersionInfo> versionInfos;
+ public final Map<String, PackageSetting> lastStaticSharedLibSettings;
private ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, InstallArgs> installArgs,
Map<String, PackageInstalledInfo> installResults,
- Map<String, PrepareResult> preparedPackages) {
+ Map<String, PrepareResult> preparedPackages,
+ Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+ Map<String, PackageParser.Package> allPackages,
+ Map<String, VersionInfo> versionInfos,
+ Map<String, PackageSetting> lastStaticSharedLibSettings) {
this.scannedPackages = scannedPackages;
this.installArgs = installArgs;
this.installResults = installResults;
this.preparedPackages = preparedPackages;
+ this.sharedLibrarySource = sharedLibrarySource;
+ this.allPackages = allPackages;
+ this.versionInfos = versionInfos;
+ this.lastStaticSharedLibSettings = lastStaticSharedLibSettings;
+ }
+
+ private ReconcileRequest(Map<String, ScanResult> scannedPackages,
+ Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+ Map<String, PackageParser.Package> allPackages,
+ Map<String, VersionInfo> versionInfos,
+ Map<String, PackageSetting> lastStaticSharedLibSettings) {
+ this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
+ Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
+ lastStaticSharedLibSettings);
}
}
private static class ReconcileFailure extends PackageManagerException {
@@ -15258,6 +15191,9 @@
ReconcileFailure(int reason, String message) {
super(reason, "Reconcile failed: " + message);
}
+ ReconcileFailure(PackageManagerException e) {
+ this(e.error, e.getMessage());
+ }
}
/**
@@ -15267,49 +15203,91 @@
private static class ReconciledPackage {
public final PackageSetting pkgSetting;
public final ScanResult scanResult;
- public final UserHandle installForUser;
- public final String volumeUuid;
// TODO: Remove install-specific details from the reconcile result
public final PackageInstalledInfo installResult;
- public final PrepareResult prepareResult;
- @PackageManager.InstallFlags
- public final int installFlags;
- public final InstallArgs installArgs;
+ @Nullable public final PrepareResult prepareResult;
+ @Nullable public final InstallArgs installArgs;
public final DeletePackageAction deletePackageAction;
+ public final List<SharedLibraryInfo> allowedSharedLibraryInfos;
+ public final SigningDetails signingDetails;
+ public final boolean sharedUserSignaturesChanged;
+ public ArrayList<SharedLibraryInfo> collectedSharedLibraryInfos;
+ public final boolean removeAppKeySetData;
private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
- UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
- String volumeUuid, PrepareResult prepareResult, ScanResult scanResult,
- DeletePackageAction deletePackageAction) {
+ PackageInstalledInfo installResult,
+ PrepareResult prepareResult, ScanResult scanResult,
+ DeletePackageAction deletePackageAction,
+ List<SharedLibraryInfo> allowedSharedLibraryInfos,
+ SigningDetails signingDetails,
+ boolean sharedUserSignaturesChanged,
+ boolean removeAppKeySetData) {
this.installArgs = installArgs;
this.pkgSetting = pkgSetting;
- this.installForUser = installForUser;
this.installResult = installResult;
- this.installFlags = installFlags;
- this.volumeUuid = volumeUuid;
this.prepareResult = prepareResult;
this.scanResult = scanResult;
this.deletePackageAction = deletePackageAction;
+ this.allowedSharedLibraryInfos = allowedSharedLibraryInfos;
+ this.signingDetails = signingDetails;
+ this.sharedUserSignaturesChanged = sharedUserSignaturesChanged;
+ this.removeAppKeySetData = removeAppKeySetData;
+ }
+
+ public boolean hasDynamicSharedLibraries() {
+ return !ArrayUtils.isEmpty(allowedSharedLibraryInfos)
+ && allowedSharedLibraryInfos.get(0).getType() != SharedLibraryInfo.TYPE_STATIC;
}
}
@GuardedBy("mPackages")
private static Map<String, ReconciledPackage> reconcilePackagesLocked(
- final ReconcileRequest request)
+ final ReconcileRequest request, KeySetManagerService ksms)
throws ReconcileFailure {
- Map<String, ReconciledPackage> result = new ArrayMap<>(request.scannedPackages.size());
- for (String installPackageName : request.installArgs.keySet()) {
- final ScanResult scanResult = request.scannedPackages.get(installPackageName);
+ final Map<String, ScanResult> scannedPackages = request.scannedPackages;
+
+ final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
+
+ // make a copy of the existing set of packages so we can combine them with incoming packages
+ final ArrayMap<String, PackageParser.Package> combinedPackages =
+ new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
+ combinedPackages.putAll(request.allPackages);
+
+ final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
+ new ArrayMap<>();
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+
+ // add / replace existing with incoming packages
+ combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg);
+
+ // in the first pass, we'll build up the set of incoming shared libraries
+ final List<SharedLibraryInfo> allowedSharedLibInfos =
+ getAllowedSharedLibInfos(scanResult, request.sharedLibrarySource);
+ final SharedLibraryInfo staticLib = scanResult.staticSharedLibraryInfo;
+ if (allowedSharedLibInfos != null) {
+ for (SharedLibraryInfo info : allowedSharedLibInfos) {
+ if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) {
+ throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
+ + " is being installed twice in this set!");
+ }
+ }
+ }
+
+ // the following may be null if we're just reconciling on boot (and not during install)
final InstallArgs installArgs = request.installArgs.get(installPackageName);
final PackageInstalledInfo res = request.installResults.get(installPackageName);
final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
- if (scanResult == null || installArgs == null || res == null) {
- throw new ReconcileFailure(
- "inputs not balanced; missing argument for " + installPackageName);
+ final boolean isInstall = installArgs != null;
+ if (isInstall && (res == null || prepareResult == null)) {
+ throw new ReconcileFailure("Reconcile arguments are not balanced for "
+ + installPackageName + "!");
}
+
final DeletePackageAction deletePackageAction;
// we only want to try to delete for non system apps
- if (prepareResult.replace && !prepareResult.system) {
+ if (isInstall && prepareResult.replace && !prepareResult.system) {
final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA
| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
@@ -15324,17 +15302,252 @@
} else {
deletePackageAction = null;
}
+
+ final int scanFlags = scanResult.request.scanFlags;
+ final int parseFlags = scanResult.request.parseFlags;
+ final PackageParser.Package pkg = scanResult.request.pkg;
+
+ final PackageSetting disabledPkgSetting = scanResult.request.disabledPkgSetting;
+ final PackageSetting lastStaticSharedLibSetting =
+ request.lastStaticSharedLibSettings.get(installPackageName);
+ final PackageSetting signatureCheckPs =
+ (prepareResult != null && lastStaticSharedLibSetting != null)
+ ? lastStaticSharedLibSetting
+ : scanResult.pkgSetting;
+ boolean removeAppKeySetData = false;
+ boolean sharedUserSignaturesChanged = false;
+ SigningDetails signingDetails = null;
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ } else {
+ if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Package " + pkg.packageName + " upgrade keys do not match the "
+ + "previously installed version");
+ } else {
+ String msg = "System package " + pkg.packageName
+ + " signature changed; retaining data.";
+ reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+ signingDetails = pkg.mSigningDetails;
+ } else {
+ try {
+ final VersionInfo versionInfo = request.versionInfos.get(installPackageName);
+ final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
+ final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+ final boolean compatMatch = verifySignatures(signatureCheckPs,
+ disabledPkgSetting, pkg.mSigningDetails, compareCompat, compareRecover);
+ // The new KeySets will be re-added later in the scanning process.
+ if (compatMatch) {
+ removeAppKeySetData = true;
+ }
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ signingDetails = pkg.mSigningDetails;
+
+
+ // if this is is a sharedUser, check to see if the new package is signed by a
+ // newer
+ // signing certificate than the existing one, and if so, copy over the new
+ // details
+ if (signatureCheckPs.sharedUser != null) {
+ if (pkg.mSigningDetails.hasAncestor(
+ signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
+ signatureCheckPs.sharedUser.signatures.mSigningDetails =
+ pkg.mSigningDetails;
+ }
+ if (signatureCheckPs.sharedUser.signaturesChanged == null) {
+ signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
+ }
+ }
+ } catch (PackageManagerException e) {
+ if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(e);
+ }
+ signingDetails = pkg.mSigningDetails;
+
+ // If the system app is part of a shared user we allow that shared user to
+ // change
+ // signatures as well as part of an OTA. We still need to verify that the
+ // signatures
+ // are consistent within the shared user for a given boot, so only allow
+ // updating
+ // the signatures on the first package scanned for the shared user (i.e. if the
+ // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
+ if (signatureCheckPs.sharedUser != null) {
+ final Signature[] sharedUserSignatures =
+ signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
+ if (signatureCheckPs.sharedUser.signaturesChanged != null
+ && compareSignatures(sharedUserSignatures,
+ pkg.mSigningDetails.signatures)
+ != PackageManager.SIGNATURE_MATCH) {
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
+ // Mismatched signatures is an error and silently skipping system
+ // packages will likely break the device in unforeseen ways.
+ // However,
+ // we allow the device to boot anyway because, prior to P,
+ // vendors were
+ // not expecting the platform to crash in this situation.
+ throw new ReconcileFailure(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: "
+ + scanResult.pkgSetting.sharedUser);
+ } else {
+ // Treat mismatched signatures on system packages using a shared
+ // UID as
+ // fatal for the system overall, rather than just failing to install
+ // whichever package happened to be scanned later.
+ throw new IllegalStateException(
+ "Signature mismatch on system package "
+ + pkg.packageName + " for shared user "
+ + scanResult.pkgSetting.sharedUser);
+ }
+ }
+
+ sharedUserSignaturesChanged = true;
+ signatureCheckPs.sharedUser.signatures.mSigningDetails =
+ pkg.mSigningDetails;
+ signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
+ }
+ // File a report about this.
+ String msg = "System package " + pkg.packageName
+ + " signature changed; retaining data.";
+ reportSettingsProblem(Log.WARN, msg);
+ } catch (IllegalArgumentException e) {
+ // should never happen: certs matched when checking, but not when comparing
+ // old to new for sharedUser
+ throw new RuntimeException(
+ "Signing certificates comparison made on incomparable signing details"
+ + " but somehow passed verifySignatures!", e);
+ }
+ }
+
result.put(installPackageName,
- new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
- res, installArgs.installFlags, installArgs.volumeUuid,
- request.preparedPackages.get(installPackageName), scanResult,
- deletePackageAction));
+ new ReconciledPackage(installArgs, scanResult.pkgSetting,
+ res, request.preparedPackages.get(installPackageName), scanResult,
+ deletePackageAction, allowedSharedLibInfos, signingDetails,
+ sharedUserSignaturesChanged, removeAppKeySetData));
}
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ // Check all shared libraries and map to their actual file path.
+ // We only do this here for apps not on a system dir, because those
+ // are the only ones that can fail an install due to this. We
+ // will take care of the system apps by updating all of their
+ // library paths after the scan is done. Also during the initial
+ // scan don't update any libs as we do this wholesale after all
+ // apps are scanned to avoid dependency based scanning.
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+ if ((scanResult.request.scanFlags & SCAN_BOOTING) != 0
+ || (scanResult.request.parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ continue;
+ }
+ try {
+ result.get(installPackageName).collectedSharedLibraryInfos =
+ collectSharedLibraryInfos(scanResult.request.pkg, combinedPackages,
+ request.sharedLibrarySource, incomingSharedLibraries);
+
+ } catch (PackageManagerException e) {
+ throw new ReconcileFailure(e.error, e.getMessage());
+ }
+ }
+
return result;
}
+ private static List<SharedLibraryInfo> getAllowedSharedLibInfos(
+ ScanResult scanResult,
+ Map<String, LongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
+ final PackageParser.Package pkg = scanResult.pkgSetting.pkg;
+ if (scanResult.staticSharedLibraryInfo == null
+ && scanResult.dynamicSharedLibraryInfos == null) {
+ return null;
+ }
+
+ // Any app can add new static shared libraries
+ if (scanResult.staticSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.staticSharedLibraryInfo);
+ }
+ final boolean hasDynamicLibraries =
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ && scanResult.dynamicSharedLibraryInfos != null;
+ if (!hasDynamicLibraries) {
+ return null;
+ }
+ final ArrayList<SharedLibraryInfo> infos =
+ new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size());
+ final boolean updatedSystemApp = pkg.isUpdatedSystemApp();
+ for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) {
+ String name = info.getName();
+ boolean allowed = false;
+ if (updatedSystemApp) {
+ // New library entries can only be added through the
+ // system image. This is important to get rid of a lot
+ // of nasty edge cases: for example if we allowed a non-
+ // system update of the app to add a library, then uninstalling
+ // the update would make the library go away, and assumptions
+ // we made such as through app install filtering would now
+ // have allowed apps on the device which aren't compatible
+ // with it. Better to just have the restriction here, be
+ // conservative, and create many fewer cases that can negatively
+ // impact the user experience.
+ final PackageSetting sysPs = scanResult.request.disabledPkgSetting;
+ if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
+ for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
+ if (name.equals(sysPs.pkg.libraryNames.get(j))) {
+ allowed = true;
+ break;
+ }
+ }
+ }
+ } else {
+ allowed = true;
+ }
+ if (allowed) {
+ if (sharedLibExists(
+ name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " library "
+ + name + " already exists; skipping");
+ continue;
+ }
+ infos.add(info);
+ } else {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
+ + name + " that is not declared on system image; skipping");
+ continue;
+ }
+ }
+ return infos;
+ }
+
+ /**
+ * Returns false if the adding shared library already exists in the map and so could not be
+ * added.
+ */
+ private static boolean addSharedLibraryToPackageVersionMap(
+ Map<String, LongSparseArray<SharedLibraryInfo>> target,
+ SharedLibraryInfo library) {
+ final String name = library.getName();
+ if (target.containsKey(name)) {
+ if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
+ // We've already added this non-version-specific library to the map.
+ return false;
+ } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
+ // We've already added this version of a version-specific library to the map.
+ return false;
+ }
+ } else {
+ target.put(name, new LongSparseArray<>());
+ }
+ target.get(name).put(library.getLongVersion(), library);
+ return true;
+ }
+
@GuardedBy("mPackages")
- private boolean commitPackagesLocked(final CommitRequest request) {
+ private void commitPackagesLocked(final CommitRequest request) {
// TODO: remove any expected failures from this method; this should only be able to fail due
// to unavoidable errors (I/O, etc.)
for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
@@ -15470,19 +15683,9 @@
}
}
-
-
- try {
- prepareScanResultLocked(scanResult);
- commitScanResultLocked(scanResult);
- } catch (PackageManagerException e) {
- unprepareScanResultLocked(scanResult);
- res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
- res.setError("Package couldn't be installed in " + pkg.codePath, e);
- return false;
- }
+ commitReconciledScanResultLocked(reconciledPkg);
updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
- res, reconciledPkg.installForUser, reconciledPkg.installArgs.installReason);
+ res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
@@ -15505,7 +15708,6 @@
updateInstantAppInstallerLocked(packageName);
}
}
- return true;
}
/**
@@ -15527,12 +15729,16 @@
*
* Failure at any phase will result in a full failure to install all packages.
*/
- @GuardedBy({"mInstallLock", "mPackages"})
+ @GuardedBy("mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
- final Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
+ final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
+ final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
+ final Map<String, PackageSetting> lastStaticSharedLibSettings =
+ new ArrayMap<>(requests.size());
+ boolean success = false;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
for (InstallRequest request : requests) {
@@ -15564,33 +15770,47 @@
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user);
for (ScanResult result : scanResults) {
- if (null != scans.put(result.pkgSetting.pkg.packageName, result)) {
+ if (null != preparedScans.put(result.pkgSetting.pkg.packageName, result)) {
request.installResult.setError(
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
"Duplicate package " + result.pkgSetting.pkg.packageName
+ " in multi-package install request.");
return;
}
+ prepareScanResultLocked(result);
+ versionInfos.put(result.pkgSetting.pkg.packageName,
+ getSettingsVersionForPackage(result.pkgSetting.pkg));
+ if (result.staticSharedLibraryInfo != null) {
+ final PackageSetting sharedLibLatestVersionSetting =
+ getSharedLibLatestVersionSetting(result);
+ if (sharedLibLatestVersionSetting != null) {
+ lastStaticSharedLibSettings.put(result.pkgSetting.pkg.packageName,
+ sharedLibLatestVersionSetting);
+ }
+ }
+ prepareScanResultLocked(result);
}
} catch (PackageManagerException e) {
request.installResult.setError("Scanning Failed.", e);
return;
}
}
- ReconcileRequest reconcileRequest = new ReconcileRequest(scans, installArgs,
+ ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
installResults,
- prepareResults);
+ prepareResults,
+ mSharedLibraries,
+ Collections.unmodifiableMap(mPackages), versionInfos,
+ lastStaticSharedLibSettings);
CommitRequest commitRequest = null;
synchronized (mPackages) {
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = reconcilePackagesLocked(reconcileRequest);
+ reconciledPackages = reconcilePackagesLocked(
+ reconcileRequest, mSettings.mKeySetManagerService);
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
- // TODO(b/109941548): add more concrete failure reasons
request.installResult.setError("Reconciliation failed...", e);
- // TODO: return any used system resources
}
return;
} finally {
@@ -15600,10 +15820,8 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
commitRequest = new CommitRequest(reconciledPackages,
sUserManager.getUserIds());
- if (!commitPackagesLocked(commitRequest)) {
- cleanUpCommitFailuresLocked(commitRequest);
- return;
- }
+ commitPackagesLocked(commitRequest);
+ success = true;
} finally {
for (PrepareResult result : prepareResults.values()) {
if (result.freezer != null) {
@@ -15615,6 +15833,11 @@
}
executePostCommitSteps(commitRequest);
} finally {
+ if (!success) {
+ for (ScanResult result : preparedScans.values()) {
+ unprepareScanResultLocked(result);
+ }
+ }
for (PrepareResult result : prepareResults.values()) {
if (result.freezer != null) {
result.freezer.close();
@@ -15631,8 +15854,8 @@
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
- final boolean instantApp =
- ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+ final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
+ & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
final String packageName = pkg.packageName;
prepareAppDataAfterInstallLIF(pkg);
@@ -15650,7 +15873,7 @@
// can be used for optimizations.
mArtManagerService.prepareAppProfiles(
pkg,
- resolveUserIds(reconciledPkg.installForUser.getIdentifier()),
+ resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),
/* updateReferenceProfileContent= */ true);
// Check whether we need to dexopt the app.
@@ -15701,109 +15924,6 @@
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(packageName);
}
-
- }
-
- private void cleanUpCommitFailuresLocked(CommitRequest request) {
- final Map<String, ReconciledPackage> reconciledPackages = request.reconciledPackages;
- final int[] allUsers = request.mAllUsers;
- for (ReconciledPackage reconciledPackage : reconciledPackages.values()) {
- final String pkgName1 = reconciledPackage.pkgSetting.pkg.packageName;
- if (reconciledPackage.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- reconciledPackage.installResult.setError(
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR, "Commit failed...");
- }
- final PackageParser.Package oldPackage =
- reconciledPackage.prepareResult.existingPackage;
- final PackageParser.Package newPackage = reconciledPackage.pkgSetting.pkg;
- if (reconciledPackage.prepareResult.system) {
- // Re installation failed. Restore old information
- // Remove new pkg information
- if (newPackage != null) {
- removeInstalledPackageLI(newPackage, true);
- }
- // Add back the old system package
- PackageParser.Package restoredPkg = null;
- try {
- final List<ScanResult> restoreResults = scanPackageTracedLI(oldPackage,
- reconciledPackage.prepareResult.parseFlags, SCAN_UPDATE_SIGNATURE, 0,
- reconciledPackage.installForUser);
- commitSuccessfulScanResults(restoreResults);
- restoredPkg = restoreResults.get(0).pkgSetting.pkg;
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
- }
- synchronized (mPackages) {
- final boolean disabledSystem;
- // Remove existing system package
- removePackageLI(reconciledPackage.scanResult.pkgSetting.pkg, true);
- disabledSystem = disableSystemPackageLPw(
- reconciledPackage.scanResult.pkgSetting.pkg, restoredPkg);
- if (disabledSystem) {
- enableSystemPackageLPw(restoredPkg);
- }
- // Ensure the installer package name up to date
- setInstallerPackageNameLPw(reconciledPackage.scanResult.pkgSetting.pkg,
- reconciledPackage.installArgs.installerPackageName);
- // Update permissions for restored package
- mPermissionManager.updatePermissions(
- restoredPkg.packageName, restoredPkg, false, mPackages.values(),
- mPermissionCallback);
- mSettings.writeLPr();
- }
- Slog.i(TAG, "Successfully restored package : " + restoredPkg.packageName
- + " after failed upgrade");
- } else if (reconciledPackage.prepareResult.replace) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName1);
-
- // Revert all internal state mutations and added folders for the failed install
- boolean deletedPkg = deletePackageLIF(pkgName1, null, true,
- allUsers, /*TODO: deleteFlags*/ 0,
- reconciledPackage.installResult.removedInfo, true, null);
-
- // Restore the old package
- if (deletedPkg) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + oldPackage);
- File restoreFile = new File(oldPackage.codePath);
- // Parse old package
- boolean oldExternal = isExternal(oldPackage);
- int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
- | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
- int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
- try {
- scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags,
- /* origUpdateTime */ System.currentTimeMillis(), null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to restore package : " + pkgName1
- + " after failed upgrade: "
- + e.getMessage());
- return;
- }
-
- synchronized (mPackages) {
- // Ensure the installer package name up to date
- setInstallerPackageNameLPw(oldPackage,
- reconciledPackage.installArgs.installerPackageName);
-
- // Update permissions for restored package
- mPermissionManager.updatePermissions(
- oldPackage.packageName, oldPackage, false, mPackages.values(),
- mPermissionCallback);
-
- mSettings.writeLPr();
- }
-
- Slog.i(TAG, "Successfully restored package : " + pkgName1
- + " after failed upgrade");
- }
- } else {
- // Remove package from internal structures, but keep around any
- // data that might have already existed
- deletePackageLIF(pkgName1, UserHandle.ALL, false, null,
- PackageManager.DELETE_KEEP_DATA,
- reconciledPackage.installResult.removedInfo, true, null);
- }
- }
}
/**
@@ -16284,15 +16404,6 @@
throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
"Error deriving application ABI");
}
-
- // Shared libraries for the package need to be updated.
- synchronized (mPackages) {
- try {
- updateSharedLibrariesLPr(pkg, null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
- }
- }
}
if (!args.doRename(res.returnCode, pkg)) {
@@ -17856,21 +17967,14 @@
public final PackageRemovedInfo outInfo;
public final int flags;
public final UserHandle user;
- /**
- * True if this package is an unupdated system app that may be deleted by the system.
- * When true, disabledPs will be null.
- */
- public final boolean mayDeleteUnupdatedSystemApp;
private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
- PackageRemovedInfo outInfo, int flags, UserHandle user,
- boolean mayDeleteUnupdatedSystemApp) {
+ PackageRemovedInfo outInfo, int flags, UserHandle user) {
this.deletingPs = deletingPs;
this.disabledPs = disabledPs;
this.outInfo = outInfo;
this.flags = flags;
this.user = user;
- this.mayDeleteUnupdatedSystemApp = mayDeleteUnupdatedSystemApp;
}
}
@@ -17886,24 +17990,22 @@
if (ps == null) {
return null;
}
- boolean mayDeleteUnupdatedSystemApp = false;
if (isSystemApp(ps)) {
if (ps.parentPackageName != null) {
Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
return null;
}
- if (((flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
- && user.getIdentifier() != UserHandle.USER_ALL) {
- mayDeleteUnupdatedSystemApp = true;
- } else if (disabledPs == null) {
- // Confirmed if the system package has been updated
- // An updated system app can be deleted. This will also have to restore
- // the system pkg from system partition
- // reader
+ final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0;
+ final boolean deleteAllUsers =
+ user == null || user.getIdentifier() == UserHandle.USER_ALL;
+ if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.packageName);
return null;
}
+ // Confirmed if the system package has been updated
+ // An updated system app can be deleted. This will also have to restore
+ // the system pkg from system partition reader
}
final int parentReferenceCount =
(ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
@@ -17918,8 +18020,7 @@
}
}
}
- return new DeletePackageAction(ps, disabledPs, outInfo, flags, user,
- mayDeleteUnupdatedSystemApp);
+ return new DeletePackageAction(ps, disabledPs, outInfo, flags, user);
}
/*
@@ -17993,8 +18094,8 @@
if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {
unsuspendForSuspendingPackage(packageName, userId);
}
-
- if (!systemApp || action.mayDeleteUnupdatedSystemApp) {
+ if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
+ && userId != UserHandle.USER_ALL) {
// The caller is asking that the package only be deleted for a single
// user. To do this, we just mark its uninstalled state and delete
// its data. If this is a system app, we only allow this to happen if
@@ -18017,13 +18118,7 @@
// We need to set it back to 'installed' so the uninstall
// broadcasts will be sent correctly.
if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
- if (userId != UserHandle.USER_ALL) {
- ps.setInstalled(true, userId);
- } else {
- for (int origUserId : outInfo.origUsers) {
- ps.setInstalled(true, origUserId);
- }
- }
+ ps.setInstalled(true, userId);
mSettings.writeKernelMappingLPr(ps);
}
} else {
@@ -19484,6 +19579,17 @@
return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
}
+ /**
+ * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing
+ * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}.
+ */
+ public void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo,
+ int userId) {
+ Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED)
+ .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo);
+ mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
+ }
+
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
UserManagerService ums = UserManagerService.getInstance();
if (ums != null) {
@@ -20124,16 +20230,21 @@
ContentObserver co = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- mWebInstantAppsDisabled =
- (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
- (Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
+ final boolean ephemeralFeatureDisabled =
+ Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final boolean instantAppsDisabledForUser =
+ ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
+ Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
+ mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
+ }
}
};
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
- false, co, UserHandle.USER_SYSTEM);
+ false, co, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
- .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
+ .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
co.onChange(true);
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 37a35a2..357872e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -630,9 +630,13 @@
pw.print("=");
}
pw.print(info.packageName);
- if (showVersionCode && !isApex) {
+ if (showVersionCode) {
pw.print(" versionCode:");
- pw.print(info.applicationInfo.versionCode);
+ if (info.applicationInfo != null) {
+ pw.print(info.applicationInfo.versionCode);
+ } else {
+ pw.print(info.versionCode);
+ }
}
if (listInstaller && !isApex) {
pw.print(" installer=");
@@ -2779,7 +2783,7 @@
pw.println(" Prints all system libraries.");
pw.println("");
pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
- pw.println(" [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
+ pw.println(" [--show-versioncode] [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
@@ -2792,6 +2796,7 @@
pw.println(" -l: ignored (used for compatibility with older releases)");
pw.println(" -U: also show the package UID");
pw.println(" -u: also include uninstalled packages");
+ pw.println(" --show-versioncode: also show the version code");
pw.println(" --apex-only: only show APEX packages");
pw.println(" --uid UID: filter to only show packages with the given UID");
pw.println(" --user USER_ID: only list packages belonging to the given user");
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 33a9650..e68c238 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -875,15 +875,13 @@
public String getClassLoaderContext() { return mClassLoaderContext; }
- @VisibleForTesting
- /* package */ boolean isUnknownClassLoaderContext() {
+ public boolean isUnknownClassLoaderContext() {
// The class loader context may be unknown if we loaded the data from a previous version
// which didn't save the context.
return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
}
- @VisibleForTesting
- /* package */ boolean isVariableClassLoaderContext() {
+ public boolean isVariableClassLoaderContext() {
return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 164af38..789664d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1193,9 +1193,9 @@
if (pm.checkPermission(fgPerm, pkg.packageName)
== PackageManager.PERMISSION_GRANTED) {
// Upgrade the app-op state of the fg permission to allow bg access
- mContext.getSystemService(AppOpsManager.class).setMode(
+ mContext.getSystemService(AppOpsManager.class).setUidMode(
AppOpsManager.permissionToOp(fgPerm), uid,
- pkg.packageName, AppOpsManager.MODE_ALLOWED);
+ AppOpsManager.MODE_ALLOWED);
break;
}
@@ -1205,8 +1205,8 @@
String bgPerm = getBackgroundPermission(permission);
if (bgPerm == null) {
if (op != null) {
- mContext.getSystemService(AppOpsManager.class).setMode(op, uid,
- pkg.packageName, AppOpsManager.MODE_ALLOWED);
+ mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid,
+ AppOpsManager.MODE_ALLOWED);
}
} else {
int mode;
@@ -1217,8 +1217,7 @@
mode = AppOpsManager.MODE_FOREGROUND;
}
- mContext.getSystemService(AppOpsManager.class).setMode(op, uid,
- pkg.packageName, mode);
+ mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
}
if (DEBUG) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b58c8116..93964cb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -27,7 +27,6 @@
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.permissionToOp;
import static android.app.AppOpsManager.permissionToOpCode;
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -1073,9 +1072,8 @@
AppOpsManagerInternal appOpsInternal = LocalServices.getService(
AppOpsManagerInternal.class);
- appOpsInternal.setMode(permissionToOpCode(permission),
- getUid(userId, getAppId(pkg.applicationInfo.uid)), pkg.packageName, mode,
- (pkg.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0);
+ appOpsInternal.setUidMode(permissionToOpCode(permission),
+ getUid(userId, getAppId(pkg.applicationInfo.uid)), mode);
}
/**
@@ -1345,8 +1343,12 @@
sourcePermNum++) {
String sourcePerm = sourcePerms.valueAt(sourcePermNum);
- if (appOpsManager.unsafeCheckOpNoThrow(permissionToOp(sourcePerm),
- getUid(userId, getAppId(pkg.applicationInfo.uid)), pkgName)
+ if (ps.hasRuntimePermission(sourcePerm, userId)
+ && ps.getRuntimePermissionState(sourcePerm, userId)
+ .isGranted()
+ && appOpsManager.unsafeCheckOpNoThrow(
+ permissionToOp(sourcePerm), getUid(userId,
+ getAppId(pkg.applicationInfo.uid)), pkgName)
== MODE_ALLOWED) {
setAppOpMode(sourcePerm, pkg, userId, MODE_FOREGROUND);
}
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
new file mode 100644
index 0000000..076c94c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "GtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "com.google.android.permission.gts.DefaultPermissionGrantPolicyTest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 4ec8b87..30aa528 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1563,11 +1563,16 @@
return mBatteryStatsHelper;
}
+ private long milliAmpHrsToNanoAmpSecs(double mAh) {
+ final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
+ return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
+ }
+
private void pullDeviceCalculatedPowerUse(int tagId,
long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
BatteryStatsHelper bsHelper = getBatteryStatsHelper();
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeFloat((float) bsHelper.getComputedPower());
+ e.writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()));
pulledData.add(e);
}
@@ -1583,7 +1588,7 @@
}
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(bs.uidObj.getUid());
- e.writeFloat((float) bs.totalPowerMah);
+ e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
pulledData.add(e);
}
}
@@ -1603,7 +1608,7 @@
}
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(bs.drainType.ordinal());
- e.writeFloat((float) bs.totalPowerMah);
+ e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
pulledData.add(e);
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 02d8c0b..fc21adb 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -718,12 +718,12 @@
disabledData += " }";
final UiState state = getUiState(displayId);
- Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+ Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + ", net1=" + net1
+ ", mDisabled1=" + state.mDisabled1 + ", token=" + token
+ ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
}
final UiState state = getUiState(displayId);
- if (state.disableEquals(net1, net2)) {
+ if (!state.disableEquals(net1, net2)) {
state.setDisabled(net1, net2);
mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
if (mBar != null) {
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
new file mode 100644
index 0000000..56bcdd9
--- /dev/null
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
@@ -0,0 +1,63 @@
+/*
+ * 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.textservices;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.view.textservice.SpellCheckerInfo;
+
+import com.android.server.LocalServices;
+
+/**
+ * Local interface of {@link TextServicesManagerService} inside system server process.
+ */
+public abstract class TextServicesManagerInternal {
+ /**
+ * Returns the list of installed input methods for the specified user.
+ *
+ * <p>CAVEAT: This method is not fully implemented yet. This may return an empty list if
+ * {@code userId} for a background user is specified. Check the implementation before starting
+ * this method.</p>
+ *
+ * @param userId The user ID to be queried.
+ * @return {@link SpellCheckerInfo} that is currently selected {@code userId}.
+ */
+ @Nullable
+ public abstract SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId);
+
+ /**
+ * Fake implementation of {@link TextServicesManagerInternal}. All the methods do nothing.
+ */
+ private static final TextServicesManagerInternal NOP =
+ new TextServicesManagerInternal() {
+ @Override
+ public SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
+ return null;
+ }
+ };
+
+ /**
+ * @return Global instance if exists. Otherwise, a dummy no-op instance.
+ */
+ @NonNull
+ public static TextServicesManagerInternal get() {
+ final TextServicesManagerInternal instance =
+ LocalServices.getService(TextServicesManagerInternal.class);
+ return instance != null ? instance : NOP;
+ }
+}
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
index 23c29f8..65d5b10 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -18,20 +18,6 @@
import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.inputmethod.SubtypeLocaleUtils;
-import com.android.internal.textservice.ISpellCheckerService;
-import com.android.internal.textservice.ISpellCheckerServiceCallback;
-import com.android.internal.textservice.ISpellCheckerSession;
-import com.android.internal.textservice.ISpellCheckerSessionListener;
-import com.android.internal.textservice.ITextServicesManager;
-import com.android.internal.textservice.ITextServicesSessionListener;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -60,12 +46,27 @@
import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.SpellCheckerSubtype;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.SubtypeLocaleUtils;
+import com.android.internal.textservice.ISpellCheckerService;
+import com.android.internal.textservice.ISpellCheckerServiceCallback;
+import com.android.internal.textservice.ISpellCheckerSession;
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesManager;
+import com.android.internal.textservice.ITextServicesSessionListener;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -278,6 +279,14 @@
@Override
public void onStart() {
+ LocalServices.addService(TextServicesManagerInternal.class,
+ new TextServicesManagerInternal() {
+ @Override
+ public SpellCheckerInfo getCurrentSpellCheckerForUser(
+ @UserIdInt int userId) {
+ return mService.getCurrentSpellCheckerForUser(userId);
+ }
+ });
publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService);
}
@@ -493,6 +502,15 @@
return spellCheckerList.get(0);
}
+ @Nullable
+ private SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(userId);
+ final TextServicesData data = mUserData.get(spellCheckerOwnerUserId);
+ return data != null ? data.getCurrentSpellChecker() : null;
+ }
+ }
+
// TODO: Save SpellCheckerService by supported languages. Currently only one spell
// checker is saved.
@Override
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7c61e37..f008770 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -905,16 +905,14 @@
return;
}
if (supportsMultiDisplay(systemConnection)
- && fallbackConnection.getConnectedEngineSize() != 0) {
- fallbackConnection.forEachDisplayConnector(
- WallpaperConnection.DisplayConnector::disconnectLocked);
+ && fallbackConnection.mDisplayConnector.size() != 0) {
+ fallbackConnection.forEachDisplayConnector(connector -> {
+ if (connector.mEngine != null) {
+ connector.disconnectLocked();
+ }
+ });
fallbackConnection.mDisplayConnector.clear();
} else {
- // TODO(b/121181553) Handle wallpaper service disconnect case.
- if (fallbackConnection.mService == null) {
- Slog.w(TAG, "There is no fallback wallpaper service");
- return;
- }
fallbackConnection.appendConnectorWithCondition(display ->
fallbackConnection.isUsableDisplay(display)
&& display.getDisplayId() != DEFAULT_DISPLAY
@@ -965,6 +963,10 @@
}
void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+ if (connection.mService == null) {
+ Slog.w(TAG, "WallpaperService is not connected yet");
+ return;
+ }
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
try {
mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
@@ -980,7 +982,7 @@
wpdData.mPadding, mDisplayId);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
- if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+ if (wallpaper != null && !wallpaper.wallpaperUpdating
&& connection.getConnectedEngineSize() == 0) {
bindWallpaperComponentLocked(null /* componentName */, false /* force */,
false /* fromUser */, wallpaper, null /* reply */);
@@ -1067,8 +1069,11 @@
for (Display display : displays) {
if (tester.test(display)) {
final int displayId = display.getDisplayId();
- mDisplayConnector.append(displayId,
- new DisplayConnector(displayId));
+ final DisplayConnector connector = mDisplayConnector.get(displayId);
+ if (connector == null) {
+ mDisplayConnector.append(displayId,
+ new DisplayConnector(displayId));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5f00bcc..2cd0168 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -42,6 +42,7 @@
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.CATEGORY_SECONDARY_HOME;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
@@ -1178,7 +1179,8 @@
private boolean isHomeIntent(Intent intent) {
return ACTION_MAIN.equals(intent.getAction())
- && intent.hasCategory(CATEGORY_HOME)
+ && (intent.hasCategory(CATEGORY_HOME)
+ || intent.hasCategory(CATEGORY_SECONDARY_HOME))
&& intent.getCategories().size() == 1
&& intent.getData() == null
&& intent.getType() == null;
@@ -2484,36 +2486,20 @@
}
void setRequestedOrientation(int requestedOrientation) {
- final int displayId = getDisplayId();
- final Configuration displayConfig =
- mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
-
- final Configuration config = setOrientation(requestedOrientation,
- displayId, displayConfig, mayFreezeScreenLocked(app));
- if (config != null) {
- frozenBeforeDestroy = true;
- if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this,
- false /* deferResume */, displayId)) {
- mRootActivityContainer.resumeFocusedStacksTopActivities();
- }
- }
+ setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
task.taskId, requestedOrientation);
}
- Configuration setOrientation(int requestedOrientation, int displayId,
- Configuration displayConfig, boolean freezeScreenIfNeeded) {
+ private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
if (mAppWindowToken == null) {
Slog.w(TAG_WM,
"Attempted to set orientation of non-existing app token: " + appToken);
- return null;
+ return;
}
- mAppWindowToken.setOrientation(requestedOrientation);
-
final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
- return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
- displayId);
+ mAppWindowToken.setOrientation(requestedOrientation, binder, this);
}
int getOrientation() {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 904d9dd..b4d5d9f 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -355,7 +355,6 @@
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
null, userId, ActivityStarter.computeResolveFilterUid(
callingUid, realCallingUid, UserHandle.USER_NULL));
- // TODO: New, check if this is correct
aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
if (aInfo != null &&
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9861157..c4be1ba537 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1426,6 +1426,42 @@
}
/**
+ * Public API to check if the client is allowed to start an activity on specified display.
+ *
+ * If the target display is private or virtual, some restrictions will apply.
+ *
+ * @param displayId Target display id.
+ * @param intent Intent used to launch the activity.
+ * @param resolvedType The MIME type of the intent.
+ * @param userId The id of the user for whom the call is made.
+ * @return {@code true} if a call to start an activity on the target display should succeed and
+ * no {@link SecurityException} will be thrown, {@code false} otherwise.
+ */
+ @Override
+ public final boolean isActivityStartAllowedOnDisplay(int displayId, Intent intent,
+ String resolvedType, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final long origId = Binder.clearCallingIdentity();
+
+ try {
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = mStackSupervisor.resolveActivity(intent, resolvedType,
+ 0 /* startFlags */, null /* profilerInfo */, userId,
+ ActivityStarter.computeResolveFilterUid(callingUid, callingUid,
+ UserHandle.USER_NULL));
+ aInfo = mAmInternal.getActivityInfoForUser(aInfo, userId);
+
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.canPlaceEntityOnDisplay(displayId, callingPid, callingUid,
+ aInfo);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ /**
* This is the internal entry point for handling Activity.finish().
*
* @param token The Binder token referencing the Activity we want to finish.
@@ -5475,6 +5511,31 @@
return intent;
}
+ /**
+ * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home
+ * activities.
+ *
+ * @param preferredPackage Specify a preferred package name, otherwise use secondary home
+ * component defined in config_secondaryHomeComponent.
+ * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME}
+ */
+ Intent getSecondaryHomeIntent(String preferredPackage) {
+ final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
+ if (preferredPackage == null) {
+ // Using the component stored in config if no package name.
+ final String secondaryHomeComponent = mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomeComponent);
+ intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
+ } else {
+ intent.setPackage(preferredPackage);
+ }
+ intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
+ if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+ intent.addCategory(Intent.CATEGORY_SECONDARY_HOME);
+ }
+ return intent;
+ }
+
ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
if (info == null) return null;
ApplicationInfo newInfo = new ApplicationInfo(info);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 8d49bf3..d8b2b52 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1837,23 +1837,23 @@
return false;
}
}
-
- if (transferStartingWindow(transferFrom)) {
- return true;
- }
-
- // There is no existing starting window, and we don't want to create a splash screen, so
- // that's it!
- if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
- return false;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
- startingData = new SplashScreenStartingData(mWmService, pkg,
- theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- getMergedOverrideConfiguration());
- scheduleAddStartingWindow();
}
+
+ if (transferStartingWindow(transferFrom)) {
+ return true;
+ }
+
+ // There is no existing starting window, and we don't want to create a splash screen, so
+ // that's it!
+ if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+ return false;
+ }
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+ startingData = new SplashScreenStartingData(mWmService, pkg,
+ theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ getMergedOverrideConfiguration());
+ scheduleAddStartingWindow();
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1943efc..a5ceee2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -397,10 +397,11 @@
private final Matrix mTmpMatrix = new Matrix();
private final Region mTmpRegion = new Region();
-
/** Used for handing back size of display */
private final Rect mTmpBounds = new Rect();
+ private final Configuration mTmpConfiguration = new Configuration();
+
/** Remove this display when animation on it has completed. */
private boolean mDeferredRemoval;
@@ -1156,6 +1157,36 @@
mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
}
+ @Override
+ boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+ ConfigurationContainer requestingContainer) {
+ final Configuration config = updateOrientationFromAppTokens(
+ getRequestedOverrideConfiguration(), freezeDisplayToken, false);
+ // If display rotation class tells us that it doesn't consider app requested orientation,
+ // this display won't rotate just because of an app changes its requested orientation. Thus
+ // it indicates that this display chooses not to handle this request.
+ final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+ if (config == null) {
+ return handled;
+ }
+
+ if (handled && requestingContainer instanceof ActivityRecord) {
+ final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
+ final boolean kept = mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+ config, activityRecord, false /* deferResume */, getDisplayId());
+ activityRecord.frozenBeforeDestroy = true;
+ if (!kept) {
+ mWmService.mAtmService.mRootActivityContainer.resumeFocusedStacksTopActivities();
+ }
+ } else {
+ // We have a new configuration to push so we need to update ATMS for now.
+ // TODO: Clean up display configuration push between ATMS and WMS after unification.
+ mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+ config, null /* starting */, false /* deferResume */, getDisplayId());
+ }
+ return handled;
+ }
+
/**
* Determine the new desired orientation of this display.
*
@@ -1169,7 +1200,56 @@
return updateOrientationFromAppTokens(false /* forceUpdate */);
}
- boolean updateOrientationFromAppTokens(boolean forceUpdate) {
+ /**
+ * Update orientation of the target display, returning a non-null new Configuration if it has
+ * changed from the current orientation. If a non-null configuration is returned, someone must
+ * call {@link WindowManagerService#setNewDisplayOverrideConfiguration(Configuration,
+ * DisplayContent)} to tell the window manager it can unfreeze the screen. This will typically
+ * be done by calling {@link WindowManagerService#sendNewConfiguration(int)}.
+ */
+ Configuration updateOrientationFromAppTokens(Configuration currentConfig,
+ IBinder freezeDisplayToken, boolean forceUpdate) {
+ if (!mDisplayReady) {
+ return null;
+ }
+
+ Configuration config = null;
+ if (updateOrientationFromAppTokens(forceUpdate)) {
+ // If we changed the orientation but mOrientationChangeComplete is already true,
+ // we used seamless rotation, and we don't need to freeze the screen.
+ if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) {
+ final AppWindowToken atoken = getAppWindowToken(freezeDisplayToken);
+ if (atoken != null) {
+ atoken.startFreezingScreen();
+ }
+ }
+ config = new Configuration();
+ computeScreenConfiguration(config);
+ } else if (currentConfig != null) {
+ // No obvious action we need to take, but if our current state mismatches the
+ // activity manager's, update it, disregarding font scale, which should remain set
+ // to the value of the previous configuration.
+ // Here we're calling Configuration#unset() instead of setToDefaults() because we
+ // need to keep override configs clear of non-empty values (e.g. fontSize).
+ mTmpConfiguration.unset();
+ mTmpConfiguration.updateFrom(currentConfig);
+ computeScreenConfiguration(mTmpConfiguration);
+ if (currentConfig.diff(mTmpConfiguration) != 0) {
+ mWaitingForConfig = true;
+ setLayoutNeeded();
+ int[] anim = new int[2];
+ getDisplayPolicy().selectRotationAnimationLw(anim);
+
+ mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
+ config = new Configuration(mTmpConfiguration);
+ }
+ }
+
+ return config;
+ }
+
+
+ private boolean updateOrientationFromAppTokens(boolean forceUpdate) {
final int req = getOrientation();
if (req != mLastOrientation || forceUpdate) {
mLastOrientation = req;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 7aabc15..bcc7be4 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -329,6 +329,15 @@
return mFixedToUserRotation;
}
+ /**
+ * Returns {@code true} if this display rotation takes app requested orientation into
+ * consideration; {@code false} otherwise. For the time being the only case where this is {@code
+ * false} is when {@link #isFixedToUserRotation()} is {@code true}.
+ */
+ boolean respectAppRequestedOrientation() {
+ return !mFixedToUserRotation;
+ }
+
public int getLandscapeRotation() {
return mLandscapeRotation;
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c1e9a73..fc1c65c 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -49,7 +49,6 @@
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.Set;
import java.util.function.Consumer;
@@ -70,9 +69,9 @@
private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer =
new UpdateInputForAllWindowsConsumer();
- private int mDisplayId;
+ private final int mDisplayId;
- SurfaceControl.Transaction mInputTransaction = new SurfaceControl.Transaction();
+ private final SurfaceControl.Transaction mInputTransaction;
/**
* The set of input consumer added to the window manager by name, which consumes input events
@@ -109,6 +108,7 @@
public InputMonitor(WindowManagerService service, int displayId) {
mService = service;
mDisplayId = displayId;
+ mInputTransaction = mService.mRoot.getDisplayContent(mDisplayId).getPendingTransaction();
}
private void addInputConsumer(String name, InputConsumerImpl consumer) {
@@ -397,7 +397,7 @@
wallpaperInputConsumer.show(mInputTransaction, 0);
}
- mInputTransaction.apply();
+ dc.scheduleAnimation();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index fff42c5..8dda485 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -2,6 +2,6 @@
jjaggi@google.com
racarr@google.com
chaviw@google.com
-brycelee@google.com
+vishnun@google.com
akulian@google.com
roosa@google.com
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index ec2d673..cb9cbd6 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -134,10 +134,8 @@
mWindowManager.deferSurfaceLayout();
try {
- final int userId = mService.getCurrentUserId();
-
// Kick off the assist data request in the background before showing the target activity
- requestAssistData(recentsComponent, recentsUid, assistDataReceiver, userId);
+ requestAssistData(recentsComponent, recentsUid, assistDataReceiver);
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
@@ -164,7 +162,7 @@
.setCallingUid(recentsUid)
.setCallingPackage(recentsComponent.getPackageName())
.setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
- .setMayWait(userId)
+ .setMayWait(mService.getCurrentUserId())
.execute();
// Move the recents activity into place for the animation
@@ -221,7 +219,7 @@
* Requests assist data for the top visible activities.
*/
private void requestAssistData(ComponentName recentsComponent, int recentsUid,
- @Deprecated IAssistDataReceiver assistDataReceiver, int userId) {
+ @Deprecated IAssistDataReceiver assistDataReceiver) {
final AppOpsManager appOpsManager = (AppOpsManager)
mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
final List<IBinder> topActivities =
@@ -237,8 +235,9 @@
final ContentCaptureManagerInternal imService =
LocalServices.getService(ContentCaptureManagerInternal.class);
final IBinder activityToken = topActivities.get(activityIndex);
- if (imService == null
- || !imService.sendActivityAssistData(userId, activityToken, data)) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+ if (r != null && (imService == null
+ || !imService.sendActivityAssistData(r.mUserId, activityToken, data))) {
// Otherwise, use the provided assist data receiver
super.onAssistDataReceivedLocked(data, activityIndex, activityCount);
}
@@ -263,7 +262,10 @@
int activityCount) {
// Try to notify the intelligence service
final IBinder activityToken = topActivities.get(activityIndex);
- imService.sendActivityAssistData(userId, activityToken, data);
+ final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+ if (r != null) {
+ imService.sendActivityAssistData(r.mUserId, activityToken, data);
+ }
}
};
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d0144fd..6f92e64 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -90,17 +90,18 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.power.V1_0.PowerHint;
-import android.os.Build;
import android.os.FactoryTest;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.IntArray;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -110,6 +111,7 @@
import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ResolverActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
@@ -346,35 +348,53 @@
}
/**
- * This starts home activity on displays that can have system decorations and only if the
- * home activity can have multiple instances.
+ * This starts home activity on displays that can have system decorations based on displayId -
+ * Default display always use primary home component.
+ * For Secondary displays, the home activity must have category SECONDARY_HOME and then resolves
+ * according to the priorities listed below.
+ * - If default home is not set, always use the secondary home defined in the config.
+ * - Use currently selected primary home activity.
+ * - Use the activity in the same package as currently selected primary home activity.
+ * If there are multiple activities matched, use first one.
+ * - Use the secondary home defined in the config.
*/
boolean startHomeOnDisplay(int userId, String reason, int displayId) {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+ Intent homeIntent;
+ ActivityInfo aInfo;
+ if (displayId == DEFAULT_DISPLAY) {
+ homeIntent = mService.getHomeIntent();
+ aInfo = resolveHomeActivity(userId, homeIntent);
+ } else {
+ Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId);
+ aInfo = info.first;
+ homeIntent = info.second;
+ }
if (aInfo == null) {
return false;
}
- if (!canStartHomeOnDisplay(aInfo, displayId,
- false /* allowInstrumenting */)) {
+ if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
return false;
}
+ // Updates the home component of the intent.
+ homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+ homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
// Update the reason for ANR debugging to verify if the user activity is the one that
// actually launched.
final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
- aInfo.applicationInfo.uid);
+ aInfo.applicationInfo.uid) + ":" + displayId;
mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
displayId);
return true;
}
/**
- * This resolves the home activity info and updates the home component of the given intent.
+ * This resolves the home activity info.
* @return the home activity info if any.
*/
- private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+ @VisibleForTesting
+ ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
final int flags = ActivityManagerService.STOCK_PM_FLAGS;
final ComponentName comp = homeIntent.getComponent();
ActivityInfo aInfo = null;
@@ -400,13 +420,82 @@
return null;
}
- homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
- homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
return aInfo;
}
+ @VisibleForTesting
+ Pair<ActivityInfo, Intent> resolveSecondaryHomeActivity(int userId, int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException(
+ "resolveSecondaryHomeActivity: Should not be DEFAULT_DISPLAY");
+ }
+ // Resolve activities in the same package as currently selected primary home activity.
+ Intent homeIntent = mService.getHomeIntent();
+ ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+ if (aInfo != null) {
+ if (ResolverActivity.class.getName().equals(aInfo.name)) {
+ // Always fallback to secondary home component if default home is not set.
+ aInfo = null;
+ } else {
+ // Look for secondary home activities in the currently selected default home
+ // package.
+ homeIntent = mService.getSecondaryHomeIntent(aInfo.applicationInfo.packageName);
+ final List<ResolveInfo> resolutions = resolveActivities(userId, homeIntent);
+ final int size = resolutions.size();
+ final String targetName = aInfo.name;
+ aInfo = null;
+ for (int i = 0; i < size; i++) {
+ ResolveInfo resolveInfo = resolutions.get(i);
+ // We need to traverse all resolutions to check if the currently selected
+ // default home activity is present.
+ if (resolveInfo.activityInfo.name.equals(targetName)) {
+ aInfo = resolveInfo.activityInfo;
+ break;
+ }
+ }
+ if (aInfo == null && size > 0) {
+ // First one is the best.
+ aInfo = resolutions.get(0).activityInfo;
+ }
+ }
+ }
+
+ if (aInfo != null) {
+ if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
+ aInfo = null;
+ }
+ }
+
+ // Fallback to secondary home component.
+ if (aInfo == null) {
+ homeIntent = mService.getSecondaryHomeIntent(null);
+ aInfo = resolveHomeActivity(userId, homeIntent);
+ }
+ return Pair.create(aInfo, homeIntent);
+ }
+
+ /**
+ * Retrieve all activities that match the given intent.
+ * The list should already ordered from best to worst matched.
+ * {@link android.content.pm.PackageManager#queryIntentActivities}
+ */
+ @VisibleForTesting
+ List<ResolveInfo> resolveActivities(int userId, Intent homeIntent) {
+ List<ResolveInfo> resolutions;
+ try {
+ final String resolvedType =
+ homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+ resolutions = AppGlobals.getPackageManager().queryIntentActivities(homeIntent,
+ resolvedType, ActivityManagerService.STOCK_PM_FLAGS, userId).getList();
+
+ } catch (RemoteException e) {
+ resolutions = new ArrayList<>();
+ }
+ return resolutions;
+ }
+
boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
@@ -457,6 +546,14 @@
return true;
}
+ final boolean deviceProvisioned = Settings.Global.getInt(
+ mService.mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY && !deviceProvisioned) {
+ // Can't launch home on secondary display before device is provisioned.
+ return false;
+ }
+
final ActivityDisplay display = getActivityDisplay(displayId);
if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
// Can't launch home on display that doesn't support system decorations.
@@ -464,13 +561,9 @@
}
final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
- && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
- && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+ && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE;
if (!supportMultipleInstance) {
- // Can't launch home on other displays if it requested to be single instance. Also we
- // don't allow home applications that target before Q to have multiple home activity
- // instances because they may not be expected to have multiple home scenario and
- // haven't explicitly request for single instance.
+ // Can't launch home on secondary displays if it requested to be single instance.
return false;
}
@@ -504,11 +597,15 @@
// Force-update the orientation from the WindowManager, since we need the true configuration
// to send to the client now.
- final Configuration config = mWindowManager.updateOrientationFromAppTokens(
- getDisplayOverrideConfiguration(displayId),
- starting != null && starting.mayFreezeScreenLocked(starting.app)
- ? starting.appToken : null,
- displayId, true /* forceUpdate */);
+ final DisplayContent displayContent = mRootWindowContainer.getDisplayContent(displayId);
+ Configuration config = null;
+ if (displayContent != null) {
+ config = displayContent.updateOrientationFromAppTokens(
+ getDisplayOverrideConfiguration(displayId),
+ starting != null && starting.mayFreezeScreenLocked(starting.app)
+ ? starting.appToken : null,
+ true /* forceUpdate */);
+ }
if (starting != null && markFrozenIfConfigChanged && config != null) {
starting.frozenBeforeDestroy = true;
}
@@ -1426,6 +1523,13 @@
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ // Stacks and activities could be removed while putting activities to sleep if
+ // the app process was gone. This prevents us getting exception by accessing an
+ // invalid stack index.
+ if (stackNdx >= display.getChildCount()) {
+ continue;
+ }
+
final ActivityStack stack = display.getChildAt(stackNdx);
if (allowDelay) {
allSleep &= stack.goToSleepIfPossible(shuttingDown);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b10fd31..d334bd2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -44,6 +44,7 @@
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.IBinder;
import android.util.EventLog;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -328,6 +329,23 @@
return boundsChange;
}
+ @Override
+ public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+ ConfigurationContainer requestingContainer) {
+ if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
+ return true;
+ }
+
+ // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill
+ // it if possible.
+ // TODO: Move to TaskRecord after unification is done.
+ if (mTaskRecord != null) {
+ mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration());
+ return true;
+ }
+ return false;
+ }
+
void resize(boolean relayout, boolean forced) {
if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 11d9ebb..ee74bdf 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -996,15 +996,20 @@
* Adjusts the stack bounds if the IME is visible.
*
* @param imeWin The IME window.
+ * @param keepLastAmount Use {@code true} to keep the last adjusted amount from
+ * {@link DockedStackDividerController} for adjusting the stack bounds,
+ * Use {@code false} to reset adjusted amount as 0.
+ * @see #updateAdjustForIme(float, float, boolean)
*/
- void setAdjustedForIme(WindowState imeWin, boolean forceUpdate) {
+ void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) {
mImeWin = imeWin;
mImeGoingAway = false;
- if (!mAdjustedForIme || forceUpdate) {
+ if (!mAdjustedForIme || keepLastAmount) {
mAdjustedForIme = true;
- mAdjustImeAmount = 0f;
- mAdjustDividerAmount = 0f;
- updateAdjustForIme(0f, 0f, true /* force */);
+ DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked;
+ final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f;
+ final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f;
+ updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index c006a7b..b219419 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -66,8 +66,11 @@
// method target window will lose the focus.
return;
}
- mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
- mDisplayContent, true /* includingParents */);
+ WindowContainer parent = mDisplayContent.getParent();
+ if (parent != null) {
+ parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent,
+ true /* includingParents */);
+ }
}
};
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 651089d..32c5a3b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -31,10 +31,12 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.IBinder;
import android.util.Pools;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -698,8 +700,58 @@
}
}
+ /**
+ * Called when this container or one of its descendants changed its requested orientation, and
+ * wants this container to handle it or pass it to its parent.
+ *
+ * @param freezeDisplayToken freeze this app window token if display needs to freeze
+ * @param requestingContainer the container which orientation request has changed
+ * @return {@code true} if handled; {@code false} otherwise.
+ */
+ boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
+ @Nullable ConfigurationContainer requestingContainer) {
+ final WindowContainer parent = getParent();
+ if (parent == null) {
+ return false;
+ }
+ return parent.onDescendantOrientationChanged(freezeDisplayToken,
+ requestingContainer);
+ }
+
+ /**
+ * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
+ * parameters.
+ *
+ * @param orientation the specified orientation.
+ */
void setOrientation(int orientation) {
+ setOrientation(orientation, null /* freezeDisplayToken */,
+ null /* ActivityRecord */);
+ }
+
+ /**
+ * Sets the specified orientation of this container. It percolates this change upward along the
+ * hierarchy to let each level of the hierarchy a chance to respond to it.
+ *
+ * @param orientation the specified orientation. Needs to be one of {@link
+ * android.content.pm.ActivityInfo.ScreenOrientation}.
+ * @param freezeDisplayToken uses this token to freeze display if orientation change is not
+ * done. Display will not be frozen if this is {@code null}, which
+ * should only happen in tests.
+ * @param requestingContainer the container which orientation request has changed. Mostly used
+ * to ensure it gets correct configuration.
+ */
+ void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
+ @Nullable ConfigurationContainer requestingContainer) {
+ final boolean changed = mOrientation != orientation;
mOrientation = orientation;
+ if (!changed) {
+ return;
+ }
+ final WindowContainer parent = getParent();
+ if (parent != null) {
+ onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+ }
}
int getOrientation() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e3ced83..b6a4a51 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2383,85 +2383,6 @@
}
}
- @Override
- public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
- IBinder freezeThisOneIfNeeded, int displayId) {
- return updateOrientationFromAppTokens(currentConfig, freezeThisOneIfNeeded, displayId,
- false /* forceUpdate */);
- }
-
- public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
- IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
-
- final Configuration config;
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
- displayId, forceUpdate);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- return config;
- }
-
- /**
- * Update orientation of the target display, returning a non-null new Configuration if it has
- * changed from the current orientation. If a non-null configuration is returned, someone must
- * call {@link #setNewDisplayOverrideConfiguration(Configuration, int)} to tell the window
- * manager it can unfreeze the screen. This will typically be done by calling
- * {@link #sendNewConfiguration(int)}.
- *
- * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
- */
- private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
- IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
- if (!mDisplayReady) {
- return null;
- }
- Configuration config = null;
-
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc != null && dc.updateOrientationFromAppTokens(forceUpdate)) {
- // If we changed the orientation but mOrientationChangeComplete is already true,
- // we used seamless rotation, and we don't need to freeze the screen.
- if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
- final AppWindowToken atoken = mRoot.getAppWindowToken(freezeThisOneIfNeeded);
- if (atoken != null) {
- atoken.startFreezingScreen();
- }
- }
- config = computeNewConfigurationLocked(displayId);
-
- } else if (currentConfig != null) {
- // No obvious action we need to take, but if our current state mismatches the activity
- // manager's, update it, disregarding font scale, which should remain set to the value
- // of the previous configuration.
- // Here we're calling Configuration#unset() instead of setToDefaults() because we need
- // to keep override configs clear of non-empty values (e.g. fontSize).
- mTempConfiguration.unset();
- mTempConfiguration.updateFrom(currentConfig);
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- displayContent.computeScreenConfiguration(mTempConfiguration);
- if (currentConfig.diff(mTempConfiguration) != 0) {
- displayContent.mWaitingForConfig = true;
- displayContent.setLayoutNeeded();
- int anim[] = new int[2];
- displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
-
- startFreezingDisplayLocked(anim[0], anim[1], displayContent);
- config = new Configuration(mTempConfiguration);
- }
- }
-
- return config;
- }
-
void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
@NonNull DisplayContent dc) {
if (dc.mWaitingForConfig) {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 58fd30e..6d8fc1c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -25,6 +25,7 @@
#include <android/hardware/gnss/1.0/IGnssMeasurement.h>
#include <android/hardware/gnss/1.1/IGnssMeasurement.h>
#include <android/hardware/gnss/2.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include "hardware_legacy/power.h"
@@ -68,6 +69,25 @@
static jmethodID method_reportNavigationMessages;
static jmethodID method_reportLocationBatch;
static jmethodID method_reportGnssServiceDied;
+static jmethodID method_correctionsGetLatitudeDegrees;
+static jmethodID method_correctionsGetLongitudeDegrees;
+static jmethodID method_correctionsGetAltitudeMeters;
+static jmethodID method_correctionsGetToaGpsNanosecondsOfWeek;
+static jmethodID method_correctionsGetSingleSatCorrectionList;
+static jmethodID method_listSize;
+static jmethodID method_correctionListGet;
+static jmethodID method_correctionSatFlags;
+static jmethodID method_correctionSatConstType;
+static jmethodID method_correctionSatId;
+static jmethodID method_correctionSatCarrierFreq;
+static jmethodID method_correctionSatIsLos;
+static jmethodID method_correctionSatEpl;
+static jmethodID method_correctionSatEplUnc;
+static jmethodID method_correctionSatRefPlane;
+static jmethodID method_correctionPlaneLatDeg;
+static jmethodID method_correctionPlaneLngDeg;
+static jmethodID method_correctionPlaneAltDeg;
+static jmethodID method_correctionPlaneAzimDeg;
/*
* Save a pointer to JavaVm to attach/detach threads executing
@@ -84,14 +104,12 @@
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::hidl_vec;
+using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
using android::hardware::gnss::V1_0::GnssConstellationType;
using android::hardware::gnss::V1_0::GnssLocation;
using android::hardware::gnss::V1_0::GnssLocationFlags;
-using android::hardware::gnss::V1_0::IAGnss;
-using android::hardware::gnss::V1_0::IAGnssCallback;
-using android::hardware::gnss::V1_0::IAGnssCallback;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssBatching;
using android::hardware::gnss::V1_0::IGnssBatchingCallback;
@@ -105,7 +123,10 @@
using android::hardware::gnss::V1_0::IGnssXtra;
using android::hardware::gnss::V1_0::IGnssXtraCallback;
-using android::hardware::gnss::V1_1::IGnssCallback;
+using android::hardware::gnss::V2_0::IGnssCallback;
+using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
+using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
+using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane;
using android::hidl::base::V1_0::IBase;
@@ -122,6 +143,13 @@
using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
+using IAGnss_V1_0 = android::hardware::gnss::V1_0::IAGnss;
+using IAGnss_V2_0 = android::hardware::gnss::V2_0::IAGnss;
+using IAGnssCallback_V1_0 = android::hardware::gnss::V1_0::IAGnssCallback;
+using IAGnssCallback_V2_0 = android::hardware::gnss::V2_0::IAGnssCallback;
+
+using IMeasurementCorrections =
+ android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
@@ -145,7 +173,8 @@
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
-sp<IAGnss> agnssIface = nullptr;
+sp<IAGnss_V1_0> agnssIface = nullptr;
+sp<IAGnss_V2_0> agnssIface_V2_0 = nullptr;
sp<IGnssBatching> gnssBatchingIface = nullptr;
sp<IGnssDebug> gnssDebugIface = nullptr;
sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
@@ -155,6 +184,11 @@
sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr;
sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
+sp<IMeasurementCorrections> gnssCorrectionsIface = nullptr;
+// This boolean is needed to ensure that Gnsss Measurement Corrections related method are only
+// initalized when needed which will be few devices initially
+bool firstGnssMeasurementCorrectionInjected = false;
+
#define WAKE_LOCK_NAME "GPS"
@@ -279,6 +313,34 @@
}
}
+struct ScopedJniString {
+ ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) {
+ mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr);
+ }
+
+ ~ScopedJniString() {
+ if (mNativeString != nullptr) {
+ mEnv->ReleaseStringUTFChars(mJavaString, mNativeString);
+ }
+ }
+
+ const char* c_str() const {
+ return mNativeString;
+ }
+
+ operator hidl_string() const {
+ return hidl_string(mNativeString);
+ }
+
+private:
+ ScopedJniString(const ScopedJniString&) = delete;
+ ScopedJniString& operator=(const ScopedJniString&) = delete;
+
+ JNIEnv* mEnv;
+ jstring mJavaString;
+ const char* mNativeString;
+};
+
class ScopedJniThreadAttach {
public:
ScopedJniThreadAttach() {
@@ -415,6 +477,8 @@
// New in 1.1
Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
+ Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
+
// TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics
static const char* sNmeaString;
static size_t sNmeaStringLength;
@@ -537,6 +601,10 @@
return Void();
}
+Return<void> GnssCallback::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) {
+ return GnssCallback::gnssSetCapabilitesCb(capabilities);
+}
+
Return<void> GnssCallback::gnssAcquireWakelockCb() {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
return Void();
@@ -1001,21 +1069,21 @@
}
/*
- * AGnssCallback implements callback methods required by the IAGnss interface.
+ * AGnssCallback_V1_0 implements callback methods required by the IAGnssCallback 1.0 interface.
*/
-struct AGnssCallback : public IAGnssCallback {
+struct AGnssCallback_V1_0 : public IAGnssCallback_V1_0 {
// Methods from ::android::hardware::gps::V1_0::IAGnssCallback follow.
Return<void> agnssStatusIpV6Cb(
- const IAGnssCallback::AGnssStatusIpV6& agps_status) override;
+ const IAGnssCallback_V1_0::AGnssStatusIpV6& agps_status) override;
Return<void> agnssStatusIpV4Cb(
- const IAGnssCallback::AGnssStatusIpV4& agps_status) override;
+ const IAGnssCallback_V1_0::AGnssStatusIpV4& agps_status) override;
private:
jbyteArray convertToIpV4(uint32_t ip);
};
-Return<void> AGnssCallback::agnssStatusIpV6Cb(
- const IAGnssCallback::AGnssStatusIpV6& agps_status) {
+Return<void> AGnssCallback_V1_0::agnssStatusIpV6Cb(
+ const IAGnssCallback_V1_0::AGnssStatusIpV6& agps_status) {
JNIEnv* env = getJniEnv();
jbyteArray byteArray = nullptr;
@@ -1048,8 +1116,8 @@
return Void();
}
-Return<void> AGnssCallback::agnssStatusIpV4Cb(
- const IAGnssCallback::AGnssStatusIpV4& agps_status) {
+Return<void> AGnssCallback_V1_0::agnssStatusIpV4Cb(
+ const IAGnssCallback_V1_0::AGnssStatusIpV4& agps_status) {
JNIEnv* env = getJniEnv();
jbyteArray byteArray = nullptr;
@@ -1080,7 +1148,7 @@
return Void();
}
-jbyteArray AGnssCallback::convertToIpV4(uint32_t ip) {
+jbyteArray AGnssCallback_V1_0::convertToIpV4(uint32_t ip) {
if (INADDR_NONE == ip) {
return nullptr;
}
@@ -1100,6 +1168,23 @@
}
/*
+ * AGnssCallback_V2_0 implements callback methods required by the IAGnssCallback 2.0 interface.
+ */
+struct AGnssCallback_V2_0 : public IAGnssCallback_V2_0 {
+ // Methods from ::android::hardware::gps::V2_0::IAGnssCallback follow.
+ Return<void> agnssStatusCb(IAGnssCallback_V2_0::AGnssType type,
+ IAGnssCallback_V2_0::AGnssStatusValue status) override;
+};
+
+Return<void> AGnssCallback_V2_0::agnssStatusCb(IAGnssCallback_V2_0::AGnssType type,
+ IAGnssCallback_V2_0::AGnssStatusValue status) {
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, type, status, nullptr);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Void();
+}
+
+/*
* AGnssRilCallback implements the callback methods required by the AGnssRil
* interface.
*/
@@ -1266,11 +1351,20 @@
}
}
- auto gnssAgnss = gnssHal->getExtensionAGnss();
- if (!gnssAgnss.isOk()) {
- ALOGD("Unable to get a handle to AGnss");
+ if (gnssHal_V2_0 != nullptr) {
+ auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0();
+ if (!agnss_V2_0.isOk()) {
+ ALOGD("Unable to get a handle to AGnss_V2_0");
+ } else {
+ agnssIface_V2_0 = agnss_V2_0;
+ }
} else {
- agnssIface = gnssAgnss;
+ auto agnss_V1_0 = gnssHal->getExtensionAGnss();
+ if (!agnss_V1_0.isOk()) {
+ ALOGD("Unable to get a handle to AGnss");
+ } else {
+ agnssIface = agnss_V1_0;
+ }
}
auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
@@ -1283,6 +1377,12 @@
if (gnssHal_V2_0 != nullptr) {
// TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0
auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
+ auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
+ if (!gnssCorrections.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurementCorrections interface");
+ } else {
+ gnssCorrectionsIface = gnssCorrections;
+ }
if (!gnssMeasurement.isOk()) {
ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
} else {
@@ -1386,11 +1486,14 @@
sp<IGnssCallback> gnssCbIface = new GnssCallback();
Return<bool> result = false;
- if (gnssHal_V1_1 != nullptr) {
+ if (gnssHal_V2_0 != nullptr) {
+ result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
+ } else if (gnssHal_V1_1 != nullptr) {
result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
} else {
result = gnssHal->setCallback(gnssCbIface);
}
+
if (!result.isOk() || !result) {
ALOGE("SetCallback for Gnss Interface fails\n");
return JNI_FALSE;
@@ -1407,11 +1510,14 @@
}
}
- sp<IAGnssCallback> aGnssCbIface = new AGnssCallback();
- if (agnssIface != nullptr) {
+ if (agnssIface_V2_0 != nullptr) {
+ sp<IAGnssCallback_V2_0> aGnssCbIface = new AGnssCallback_V2_0();
+ agnssIface_V2_0->setCallback(aGnssCbIface);
+ } else if (agnssIface != nullptr) {
+ sp<IAGnssCallback_V1_0> aGnssCbIface = new AGnssCallback_V1_0();
agnssIface->setCallback(aGnssCbIface);
} else {
- ALOGI("Unable to Initialize AGnss interface\n");
+ ALOGI("Unable to initialize AGnss interface\n");
}
sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
@@ -1540,9 +1646,8 @@
return;
}
- const char *setid = env->GetStringUTFChars(setid_string, nullptr);
- agnssRilIface->setSetId((IAGnssRil_V1_0::SetIDType)type, setid);
- env->ReleaseStringUTFChars(setid_string, setid);
+ ScopedJniString jniSetId{env, setid_string};
+ agnssRilIface->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
}
static jint android_location_GnssLocationProvider_read_nmea(JNIEnv* env, jobject /* obj */,
@@ -1630,68 +1735,126 @@
env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
}
-static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
- JNIEnv* env, jobject /* obj */, jstring apn, jint apnIpType) {
- if (agnssIface == nullptr) {
- ALOGE("no AGPS interface in agps_data_conn_open");
- return;
- }
- if (apn == nullptr) {
- jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
- return;
- }
+struct AGnssDispatcher {
+ static void dataConnOpen(sp<IAGnss_V1_0> agnssIface, JNIEnv* env, jstring apn, jint apnIpType);
+ static void dataConnOpen(sp<IAGnss_V2_0> agnssIface_V2_0, JNIEnv* env, jlong networkHandle,
+ jstring apn, jint apnIpType);
- const char *apnStr = env->GetStringUTFChars(apn, nullptr);
+ template <class T>
+ static void dataConnClosed(sp<T> agnssIface);
- auto result = agnssIface->dataConnOpen(apnStr, static_cast<IAGnss::ApnIpType>(apnIpType));
+ template <class T>
+ static void dataConnFailed(sp<T> agnssIface);
+
+ template <class T, class U>
+ static void setServer(sp<T> agnssIface, JNIEnv* env, jint type, jstring hostname, jint port);
+
+private:
+ AGnssDispatcher() = delete;
+ AGnssDispatcher(const AGnssDispatcher&) = delete;
+ AGnssDispatcher& operator=(const AGnssDispatcher&) = delete;
+};
+
+void AGnssDispatcher::dataConnOpen(sp<IAGnss_V1_0> agnssIface, JNIEnv* env, jstring apn,
+ jint apnIpType) {
+ ScopedJniString jniApn{env, apn};
+ auto result = agnssIface->dataConnOpen(jniApn,
+ static_cast<IAGnss_V1_0::ApnIpType>(apnIpType));
if (!result.isOk() || !result){
ALOGE("%s: Failed to set APN and its IP type", __func__);
}
- env->ReleaseStringUTFChars(apn, apnStr);
}
-static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_closed(JNIEnv* /* env */,
- jobject /* obj */) {
- if (agnssIface == nullptr) {
- ALOGE("%s: AGPS interface not supported", __func__);
- return;
+void AGnssDispatcher::dataConnOpen(sp<IAGnss_V2_0> agnssIface_V2_0, JNIEnv* env,
+ jlong networkHandle, jstring apn, jint apnIpType) {
+ ScopedJniString jniApn{env, apn};
+ auto result = agnssIface_V2_0->dataConnOpen(static_cast<uint64_t>(networkHandle), jniApn,
+ static_cast<IAGnss_V2_0::ApnIpType>(apnIpType));
+ if (!result.isOk() || !result){
+ ALOGE("%s: Failed to set APN and its IP type", __func__);
}
+}
+template<class T>
+void AGnssDispatcher::dataConnClosed(sp<T> agnssIface) {
auto result = agnssIface->dataConnClosed();
if (!result.isOk() || !result) {
ALOGE("%s: Failed to close AGnss data connection", __func__);
}
}
-static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed(JNIEnv* /* env */,
- jobject /* obj */) {
- if (agnssIface == nullptr) {
- ALOGE("%s: AGPS interface not supported", __func__);
- return;
- }
-
+template<class T>
+void AGnssDispatcher::dataConnFailed(sp<T> agnssIface) {
auto result = agnssIface->dataConnFailed();
if (!result.isOk() || !result) {
ALOGE("%s: Failed to notify unavailability of AGnss data connection", __func__);
}
}
-static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, jobject /* obj */,
- jint type, jstring hostname, jint port) {
- if (agnssIface == nullptr) {
- ALOGE("no AGPS interface in set_agps_server");
- return;
- }
-
- const char *c_hostname = env->GetStringUTFChars(hostname, nullptr);
- auto result = agnssIface->setServer(static_cast<IAGnssCallback::AGnssType>(type),
- c_hostname,
- port);
+template <class T, class U>
+void AGnssDispatcher::setServer(sp<T> agnssIface, JNIEnv* env, jint type, jstring hostname,
+ jint port) {
+ ScopedJniString jniHostName{env, hostname};
+ auto result = agnssIface->setServer(static_cast<typename U::AGnssType>(type),
+ jniHostName, port);
if (!result.isOk() || !result) {
ALOGE("%s: Failed to set AGnss host name and port", __func__);
}
+}
- env->ReleaseStringUTFChars(hostname, c_hostname);
+static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
+ JNIEnv* env, jobject /* obj */, jlong networkHandle, jstring apn, jint apnIpType) {
+ if (apn == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
+ return;
+ }
+
+ if (agnssIface_V2_0 != nullptr) {
+ AGnssDispatcher::dataConnOpen(agnssIface_V2_0, env, networkHandle, apn, apnIpType);
+ } else if (agnssIface != nullptr) {
+ AGnssDispatcher::dataConnOpen(agnssIface, env, apn, apnIpType);
+ } else {
+ ALOGE("%s: AGPS interface not supported", __func__);
+ return;
+ }
+}
+
+static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_closed(JNIEnv* /* env */,
+ jobject /* obj */) {
+ if (agnssIface_V2_0 != nullptr) {
+ AGnssDispatcher::dataConnClosed(agnssIface_V2_0);
+ } else if (agnssIface != nullptr) {
+ AGnssDispatcher::dataConnClosed(agnssIface);
+ } else {
+ ALOGE("%s: AGPS interface not supported", __func__);
+ return;
+ }
+}
+
+static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed(JNIEnv* /* env */,
+ jobject /* obj */) {
+ if (agnssIface_V2_0 != nullptr) {
+ AGnssDispatcher::dataConnFailed(agnssIface_V2_0);
+ } else if (agnssIface != nullptr) {
+ AGnssDispatcher::dataConnFailed(agnssIface);
+ } else {
+ ALOGE("%s: AGPS interface not supported", __func__);
+ return;
+ }
+}
+
+static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, jobject /* obj */,
+ jint type, jstring hostname, jint port) {
+ if (agnssIface_V2_0 != nullptr) {
+ AGnssDispatcher::setServer<IAGnss_V2_0, IAGnssCallback_V2_0>(agnssIface_V2_0, env, type,
+ hostname, port);
+ } else if (agnssIface != nullptr) {
+ AGnssDispatcher::setServer<IAGnss_V1_0, IAGnssCallback_V1_0>(agnssIface, env, type,
+ hostname, port);
+ } else {
+ ALOGE("%s: AGPS interface not supported", __func__);
+ return;
+ }
}
static void android_location_GnssLocationProvider_send_ni_response(JNIEnv* /* env */,
@@ -1786,41 +1949,37 @@
jstring apn,
jlong networkHandle,
jshort capabilities) {
- if (agnssRilIface == nullptr) {
- ALOGE("AGnssRilInterface does not exist");
- return;
- }
-
- const char *c_apn = env->GetStringUTFChars(apn, nullptr);
- const android::hardware::hidl_string hidl_apn{c_apn};
if (agnssRilIface_V2_0 != nullptr) {
+ ScopedJniString jniApn{env, apn};
IAGnssRil_V2_0::NetworkAttributes networkAttributes = {
.networkHandle = static_cast<uint64_t>(networkHandle),
.isConnected = static_cast<bool>(connected),
.capabilities = static_cast<uint16_t>(capabilities),
- .apn = hidl_apn
+ .apn = jniApn
};
auto result = agnssRilIface_V2_0->updateNetworkState_2_0(networkAttributes);
if (!result.isOk() || !result) {
ALOGE("updateNetworkState_2_0 failed");
}
- } else {
+ } else if (agnssRilIface != nullptr) {
+ ScopedJniString jniApn{env, apn};
+ hidl_string hidlApn{jniApn};
auto result = agnssRilIface->updateNetworkState(connected,
static_cast<IAGnssRil_V1_0::NetworkType>(type), roaming);
if (!result.isOk() || !result) {
ALOGE("updateNetworkState failed");
}
- if (!hidl_apn.empty()) {
- result = agnssRilIface->updateNetworkAvailability(available, hidl_apn);
+ if (!hidlApn.empty()) {
+ result = agnssRilIface->updateNetworkAvailability(available, hidlApn);
if (!result.isOk() || !result) {
ALOGE("updateNetworkAvailability failed");
}
}
+ } else {
+ ALOGE("AGnssRilInterface does not exist");
}
-
- env->ReleaseStringUTFChars(apn, c_apn);
}
static jboolean android_location_GnssGeofenceProvider_is_geofence_supported(
@@ -1933,6 +2092,150 @@
return boolToJbool(result.isOk());
}
+static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measurement_corrections(
+ JNIEnv* env,
+ jobject obj /* clazz*/,
+ jobject correctionsObj) {
+
+ if (gnssCorrectionsIface == nullptr) {
+ ALOGW("Trying to inject GNSS corrections on a chipset that does not support them.");
+ return JNI_FALSE;
+ }
+ if (firstGnssMeasurementCorrectionInjected == false) {
+ jclass measCorrClass = env->GetObjectClass(correctionsObj);
+ method_correctionsGetLatitudeDegrees = env->GetMethodID(
+ measCorrClass,"getLatitudeDegrees", "()D");
+
+ method_correctionsGetLongitudeDegrees = env->GetMethodID(
+ measCorrClass, "getLongitudeDegrees", "()D");
+
+ method_correctionsGetAltitudeMeters = env->GetMethodID(
+ measCorrClass, "getAltitudeMeters", "()D");
+
+ method_correctionsGetToaGpsNanosecondsOfWeek = env->GetMethodID(
+ measCorrClass, "getToaGpsNanosecondsOfWeek", "()J");
+
+ method_correctionsGetSingleSatCorrectionList = env->GetMethodID(
+ measCorrClass, "getSingleSatCorrectionList", "()Ljava.util.List;");
+ }
+
+ jdouble latitudeDegreesCorr = env->CallDoubleMethod(
+ correctionsObj, method_correctionsGetLatitudeDegrees);
+ jdouble longitudeDegreesCorr = env->CallDoubleMethod(
+ correctionsObj, method_correctionsGetLongitudeDegrees);
+ jdouble altitudeDegreesCorr = env->CallDoubleMethod(
+ correctionsObj, method_correctionsGetAltitudeMeters);
+ jlong toaGpsNanosOfWeek = env->CallLongMethod(
+ correctionsObj, method_correctionsGetToaGpsNanosecondsOfWeek);
+ jobject singleSatCorrectionList = env->CallObjectMethod(correctionsObj,
+ method_correctionsGetSingleSatCorrectionList);
+
+ if (firstGnssMeasurementCorrectionInjected == false) {
+ jclass corrListClass = env->GetObjectClass(singleSatCorrectionList);
+ method_listSize = env->GetMethodID(corrListClass, "size", "()I");
+ method_correctionListGet = env->GetMethodID(
+ corrListClass, "get", "(I)Landroid/location/GnssSingleSatCorrection;");
+ }
+
+ auto len = (singleSatCorrectionList == nullptr)
+ ? 0
+ : env->CallIntMethod(singleSatCorrectionList, method_listSize);
+ hidl_vec<SingleSatCorrection> list(len);
+
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject singleSatCorrectionObj = env->CallObjectMethod(
+ singleSatCorrectionList, method_correctionListGet, i);
+
+ if (firstGnssMeasurementCorrectionInjected == false) {
+ jclass singleSatCorrClass = env->GetObjectClass(singleSatCorrectionObj);
+ method_correctionSatFlags = env->GetMethodID(
+ singleSatCorrClass, "getSingleSatCorrectionFlags", "()I");
+ method_correctionSatConstType = env->GetMethodID(
+ singleSatCorrClass, "getConstellationType", "()I");
+ method_correctionSatId= env->GetMethodID(
+ singleSatCorrClass, "getSatId", "()I");
+ method_correctionSatCarrierFreq = env->GetMethodID(
+ singleSatCorrClass, "getCarrierFrequencyHz", "()F");
+ method_correctionSatIsLos = env->GetMethodID(
+ singleSatCorrClass,"getSatIsLos", "()Z");
+ method_correctionSatEpl = env->GetMethodID(
+ singleSatCorrClass, "getExcessPathLengthMeters", "()F");
+ method_correctionSatEplUnc = env->GetMethodID(
+ singleSatCorrClass, "getExcessPathLengthUncertaintyMeters", "()F");
+ method_correctionSatRefPlane = env->GetMethodID(
+ singleSatCorrClass, "getReflectingPlane",
+ "()Landroid/location/GnssReflectingPlane;");
+ }
+
+ jint correctionFlags =
+ env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags);
+ jint constType = env->CallIntMethod(singleSatCorrectionObj,
+ method_correctionSatConstType);
+ jint satId =
+ env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
+ jfloat carrierFreqHz = env->CallFloatMethod(
+ singleSatCorrectionObj, method_correctionSatCarrierFreq);
+ jboolean satIsLos = env->CallBooleanMethod(singleSatCorrectionObj,
+ method_correctionSatIsLos);
+ jfloat eplMeters =
+ env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
+ jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj,
+ method_correctionSatEplUnc);
+ jobject reflectingPlaneObj = env->CallObjectMethod(
+ singleSatCorrectionObj, method_correctionSatRefPlane);
+
+ if (firstGnssMeasurementCorrectionInjected == false) {
+ jclass refPlaneClass = env->GetObjectClass(reflectingPlaneObj);
+ method_correctionPlaneLatDeg = env->GetMethodID(
+ refPlaneClass, "getLatitudeDegrees", "()D");
+ method_correctionPlaneLngDeg = env->GetMethodID(
+ refPlaneClass, "getLongitudeDegrees", "()D");
+ method_correctionPlaneAltDeg = env->GetMethodID(
+ refPlaneClass, "getAltitudeMeters", "()D");
+ method_correctionPlaneAzimDeg = env->GetMethodID(
+ refPlaneClass, "getAzimuthDegrees", "()D");
+ }
+
+ jdouble latitudeDegreesRefPlane = env->CallDoubleMethod(
+ reflectingPlaneObj, method_correctionPlaneLatDeg);
+ jdouble longitudeDegreesRefPlane = env->CallDoubleMethod(
+ reflectingPlaneObj, method_correctionPlaneLngDeg);
+ jdouble altitudeDegreesRefPlane = env->CallDoubleMethod(
+ reflectingPlaneObj, method_correctionPlaneAltDeg);
+ jdouble azimuthDegreeRefPlane = env->CallDoubleMethod(
+ reflectingPlaneObj, method_correctionPlaneAzimDeg);
+ ReflectingPlane reflectingPlane = {
+ .latitudeDegrees = latitudeDegreesRefPlane,
+ .longitudeDegrees = longitudeDegreesRefPlane,
+ .altitudeMeters = altitudeDegreesRefPlane,
+ .azimuthDegrees = azimuthDegreeRefPlane,
+ };
+
+ SingleSatCorrection singleSatCorrection = {
+ .singleSatCorrectionFlags = static_cast<uint16_t>(correctionFlags),
+ .constellation = static_cast<GnssConstellationType>(constType),
+ .svid = static_cast<uint16_t>(satId),
+ .carrierFrequencyHz = carrierFreqHz,
+ .satIsLos = static_cast<bool>(satIsLos),
+ .excessPathLengthMeters = eplMeters,
+ .excessPathLengthUncertaintyMeters = eplUncMeters,
+ .reflectingPlane = reflectingPlane,
+ };
+ list[i] = singleSatCorrection;
+ }
+ MeasurementCorrections measurementCorrections = {
+ .latitudeDegrees = latitudeDegreesCorr,
+ .longitudeDegrees = longitudeDegreesCorr,
+ .altitudeMeters = altitudeDegreesCorr,
+ .toaGpsNanosecondsOfWeek = static_cast<uint64_t>(toaGpsNanosOfWeek),
+ .satCorrections = list,
+ };
+
+ gnssCorrectionsIface->setCorrections(measurementCorrections);
+ firstGnssMeasurementCorrectionInjected = true;
+ return JNI_TRUE;
+}
+
static jboolean android_location_GnssNavigationMessageProvider_is_navigation_message_supported(
JNIEnv* env,
jclass clazz) {
@@ -2310,19 +2613,20 @@
};
static const JNINativeMethod sMeasurementMethods[] = {
- /* name, signature, funcPtr */
- {"native_is_measurement_supported",
- "()Z",
- reinterpret_cast<void *>(
- android_location_GnssMeasurementsProvider_is_measurement_supported)},
- {"native_start_measurement_collection",
- "(Z)Z",
- reinterpret_cast<void *>(
- android_location_GnssMeasurementsProvider_start_measurement_collection)},
- {"native_stop_measurement_collection",
- "()Z",
- reinterpret_cast<void *>(
- android_location_GnssMeasurementsProvider_stop_measurement_collection)},
+ /* name, signature, funcPtr */
+ {"native_is_measurement_supported", "()Z",
+ reinterpret_cast<void*>(
+ android_location_GnssMeasurementsProvider_is_measurement_supported)},
+ {"native_start_measurement_collection", "(Z)Z",
+ reinterpret_cast<void*>(
+ android_location_GnssMeasurementsProvider_start_measurement_collection)},
+ {"native_stop_measurement_collection", "()Z",
+ reinterpret_cast<void*>(
+ android_location_GnssMeasurementsProvider_stop_measurement_collection)},
+ {"native_inject_gnss_measurement_corrections",
+ "(Landroid/location/GnssMeasurementCorrections;)Z",
+ reinterpret_cast<void*>(
+ android_location_GnssMeasurementsProvider_inject_gnss_measurement_corrections)},
};
static const JNINativeMethod sNavigationMessageMethods[] = {
@@ -2349,7 +2653,7 @@
"(ZIZZLjava/lang/String;JS)V",
reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_update_network_state)},
{"native_agps_data_conn_open",
- "(Ljava/lang/String;I)V",
+ "(JLjava/lang/String;I)V",
reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_open)},
{"native_agps_data_conn_closed",
"()V",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bd3dfe9..409e7f4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2105,6 +2105,7 @@
return Settings.Global.getInt(mContext.getContentResolver(), name, def);
}
+ @Nullable
String settingsGlobalGetString(String name) {
return Settings.Global.getString(mContext.getContentResolver(), name);
}
@@ -13901,7 +13902,11 @@
Preconditions.checkNotNull(who, "ComponentName is null");
enforceDeviceOwner(who);
- switch (mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE)) {
+ String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
+ if (currentMode == null) {
+ currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
+ }
+ switch (currentMode) {
case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
return PRIVATE_DNS_MODE_OFF;
case ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC:
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cf03d61..046c991 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2116,30 +2116,33 @@
private void startContentCaptureService(@NonNull Context context) {
- // First check if it was explicitly enabled by Settings
- boolean explicitlySupported = false;
+ // Check if it was explicitly enabled by Settings
final String settings = Settings.Global.getString(context.getContentResolver(),
Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED);
- if (settings != null) {
- explicitlySupported = Boolean.parseBoolean(settings);
- if (explicitlySupported) {
+ if (settings == null) {
+ // Better be safe than sorry...
+ Slog.d(TAG, "ContentCaptureService disabled because its not set by OEM");
+ return;
+ }
+ switch (settings) {
+ case "always":
+ // Should be used only during development
Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings");
- } else {
- Slog.d(TAG, "ContentCaptureService explicitly disabled by Settings");
+ break;
+ case "default":
+ // Default case: check if OEM overlaid the resource that defines the service.
+ final String serviceName = context.getString(
+ com.android.internal.R.string.config_defaultContentCaptureService);
+ if (TextUtils.isEmpty(serviceName)) {
+ Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
+ return;
+ }
+ break;
+ default:
+ // Kill switch for OEMs
+ Slog.d(TAG, "ContentCaptureService disabled because its set to: " + settings);
return;
- }
}
-
- // Then check if OEM overlaid the resource that defines the service.
- if (!explicitlySupported) {
- final String serviceName = context
- .getString(com.android.internal.R.string.config_defaultContentCaptureService);
- if (TextUtils.isEmpty(serviceName)) {
- Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
- return;
- }
- }
-
traceBeginAndSlog("StartContentCaptureService");
mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS);
traceEnd();
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index 83f66c5..b8723c5 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -23,6 +23,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -86,9 +87,7 @@
mContext = application;
mShadowContext = shadowOf(application);
- // 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;
+ mUserOneId = UserHandle.USER_SYSTEM + 1;
mUserTwoId = mUserOneId + 1;
}
@@ -176,9 +175,78 @@
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.
+ /** Test that the service unregisters users when stopped. */
+ @Test
+ public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+
+ backupManagerService.stopServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isNull();
+ assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService);
+ }
+
+ /** Test that the service unregisters users when stopped. */
+ @Test
+ public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.stopServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(0);
+ }
+
+ /**
+ * 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 testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.getServiceForUserIfCallerHasPermission(
+ mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller does
+ * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
// ---------------------------------------------
// Backup agent tests
@@ -189,8 +257,9 @@
public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.dataChanged(TEST_PACKAGE);
+ backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).dataChanged(TEST_PACKAGE);
}
@@ -198,9 +267,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.dataChanged(TEST_PACKAGE);
+ backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
}
@@ -210,9 +281,10 @@
public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
IBinder agentBinder = mock(IBinder.class);
- backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+ backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);
verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
}
@@ -220,10 +292,12 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
IBinder agentBinder = mock(IBinder.class);
- backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+ backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);
verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
}
@@ -233,8 +307,9 @@
public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.agentDisconnected(TEST_PACKAGE);
+ backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
}
@@ -242,9 +317,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.agentDisconnected(TEST_PACKAGE);
+ backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
}
@@ -254,8 +331,9 @@
public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+ backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);
verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
}
@@ -263,9 +341,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+ backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);
verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
}
@@ -279,9 +359,10 @@
public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
String[] transports = {TEST_TRANSPORT};
- backupManagerService.initializeTransports(transports, /* observer */ null);
+ backupManagerService.initializeTransports(mUserOneId, transports, /* observer */ null);
verify(mUserOneService).initializeTransports(transports, /* observer */ null);
}
@@ -289,10 +370,12 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
String[] transports = {TEST_TRANSPORT};
- backupManagerService.initializeTransports(transports, /* observer */ null);
+ backupManagerService.initializeTransports(mUserTwoId, transports, /* observer */ null);
verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
}
@@ -302,8 +385,9 @@
public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE);
verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
}
@@ -311,9 +395,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE);
verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
}
@@ -323,8 +409,9 @@
public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransport();
+ backupManagerService.getCurrentTransport(mUserOneId);
verify(mUserOneService).getCurrentTransport();
}
@@ -332,9 +419,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransport();
+ backupManagerService.getCurrentTransport(mUserTwoId);
verify(mUserOneService, never()).getCurrentTransport();
}
@@ -345,8 +434,9 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransportComponent();
+ backupManagerService.getCurrentTransportComponent(mUserOneId);
verify(mUserOneService).getCurrentTransportComponent();
}
@@ -355,9 +445,11 @@
@Test
public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getCurrentTransportComponent();
+ backupManagerService.getCurrentTransportComponent(mUserTwoId);
verify(mUserOneService, never()).getCurrentTransportComponent();
}
@@ -367,8 +459,9 @@
public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.listAllTransports();
+ backupManagerService.listAllTransports(mUserOneId);
verify(mUserOneService).listAllTransports();
}
@@ -376,9 +469,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.listAllTransports();
+ backupManagerService.listAllTransports(mUserTwoId);
verify(mUserOneService, never()).listAllTransports();
}
@@ -389,8 +484,9 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.listAllTransportComponents();
+ backupManagerService.listAllTransportComponents(mUserOneId);
verify(mUserOneService).listAllTransportComponents();
}
@@ -399,32 +495,13 @@
@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);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getTransportWhitelist();
+ backupManagerService.listAllTransportComponents(mUserTwoId);
- 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();
+ verify(mUserOneService, never()).listAllTransportComponents();
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@@ -433,11 +510,13 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
backupManagerService.updateTransportAttributes(
+ mUserOneId,
transport.getTransportComponent(),
transport.transportName,
configurationIntent,
@@ -459,12 +538,15 @@
@Test
public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
backupManagerService.updateTransportAttributes(
+ mUserTwoId,
transport.getTransportComponent(),
transport.transportName,
configurationIntent,
@@ -487,8 +569,9 @@
public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+ backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
}
@@ -496,9 +579,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+ backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
}
@@ -508,11 +593,12 @@
public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
backupManagerService.selectBackupTransportAsync(
- transport.getTransportComponent(), callback);
+ mUserOneId, transport.getTransportComponent(), callback);
verify(mUserOneService)
.selectBackupTransportAsync(transport.getTransportComponent(), callback);
@@ -522,12 +608,14 @@
@Test
public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
TransportData transport = backupTransport();
ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
backupManagerService.selectBackupTransportAsync(
- transport.getTransportComponent(), callback);
+ mUserTwoId, transport.getTransportComponent(), callback);
verify(mUserOneService, never())
.selectBackupTransportAsync(transport.getTransportComponent(), callback);
@@ -538,8 +626,9 @@
public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+ backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
}
@@ -547,9 +636,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+ backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
}
@@ -559,8 +650,9 @@
public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getDestinationString(TEST_TRANSPORT);
+ backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
}
@@ -568,9 +660,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getDestinationString(TEST_TRANSPORT);
+ backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
}
@@ -580,8 +674,9 @@
public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+ backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
}
@@ -589,9 +684,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+ backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
}
@@ -601,8 +698,9 @@
public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+ backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
}
@@ -610,9 +708,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+ backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
}
@@ -620,7 +720,6 @@
// ---------------------------------------------
// Settings tests
// ---------------------------------------------
-
/**
* 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.
@@ -681,8 +780,9 @@
public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.setAutoRestore(true);
+ backupManagerService.setAutoRestore(mUserOneId, true);
verify(mUserOneService).setAutoRestore(true);
}
@@ -690,66 +790,17 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.setAutoRestore(true);
+ backupManagerService.setAutoRestore(mUserTwoId, 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 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_throwsSecurityExceptionForNonCallingUser() {
- BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- expectThrows(
- 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_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
@@ -781,8 +832,9 @@
public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+ backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
}
@@ -790,9 +842,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+ backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
}
@@ -803,9 +857,10 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.filterAppsEligibleForBackup(packages);
+ backupManagerService.filterAppsEligibleForBackup(mUserOneId, packages);
verify(mUserOneService).filterAppsEligibleForBackup(packages);
}
@@ -814,10 +869,12 @@
@Test
public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.filterAppsEligibleForBackup(packages);
+ backupManagerService.filterAppsEligibleForBackup(mUserTwoId, packages);
verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
}
@@ -1001,7 +1058,7 @@
@Test
public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
FullBackupJob job = new FullBackupJob();
backupManagerService.beginFullBackup(job);
@@ -1024,7 +1081,7 @@
@Test
public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
backupManagerService.endFullBackup();
@@ -1046,9 +1103,10 @@
public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.fullTransportBackup(packages);
+ backupManagerService.fullTransportBackup(mUserOneId, packages);
verify(mUserOneService).fullTransportBackup(packages);
}
@@ -1056,10 +1114,12 @@
/** Test that the backup service does not route methods for non-registered users. */
@Test
public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
String[] packages = {TEST_PACKAGE};
- backupManagerService.fullTransportBackup(packages);
+ backupManagerService.fullTransportBackup(mUserTwoId, packages);
verify(mUserOneService, never()).fullTransportBackup(packages);
}
@@ -1073,8 +1133,9 @@
public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0);
verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
}
@@ -1082,9 +1143,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0);
verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
}
@@ -1094,8 +1157,9 @@
public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
}
@@ -1103,9 +1167,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
}
@@ -1116,8 +1182,9 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+ backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE);
verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
}
@@ -1125,9 +1192,11 @@
/** 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 backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+ backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE);
verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
}
@@ -1140,7 +1209,7 @@
@Test
public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
backupManagerService.setBackupPassword("currentPassword", "newPassword");
@@ -1161,7 +1230,7 @@
@Test
public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
backupManagerService.hasBackupPassword();
@@ -1377,10 +1446,16 @@
throws Exception {
BackupManagerService backupManagerService =
createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
backupManagerService.acknowledgeAdbBackupOrRestore(
- /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+ mUserOneId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
verify(mUserOneService)
.acknowledgeAdbBackupOrRestore(
@@ -1395,11 +1470,18 @@
@Test
public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
throws Exception {
- BackupManagerService backupManagerService = createService();
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
backupManagerService.acknowledgeAdbBackupOrRestore(
- /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+ mUserTwoId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
verify(mUserOneService, never())
.acknowledgeAdbBackupOrRestore(
@@ -1418,7 +1500,7 @@
@Test
public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
BackupManagerService backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
File testFile = new File(mContext.getFilesDir(), "test");
testFile.createNewFile();
FileDescriptor fileDescriptor = new FileDescriptor();
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index efbcb96..bb60c27 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -71,7 +71,6 @@
import org.robolectric.shadows.ShadowContextWrapper;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.shadows.ShadowSettings;
import java.io.File;
import java.util.List;
@@ -87,6 +86,7 @@
private static final String TAG = "BMSTest";
private static final String PACKAGE_1 = "some.package.1";
private static final String PACKAGE_2 = "some.package.2";
+ private static final int USER_ID = 10;
@Mock private TransportManager mTransportManager;
private HandlerThread mBackupThread;
@@ -432,7 +432,7 @@
}
private String getSettingsTransport() {
- return ShadowSettings.ShadowSecure.getString(
+ return Settings.Secure.getString(
mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
}
@@ -979,6 +979,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1000,6 +1001,7 @@
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1022,6 +1024,7 @@
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
/* context */ null,
new Trampoline(mContext),
mBackupThread,
@@ -1041,6 +1044,7 @@
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
/* trampoline */ null,
mBackupThread,
@@ -1060,6 +1064,7 @@
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
/* backupThread */ null,
@@ -1079,6 +1084,7 @@
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1089,8 +1095,8 @@
/**
* Test checking non-null argument on {@link
- * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
- * File, TransportManager)}.
+ * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+ * File, File, TransportManager)}.
*/
@Test
public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1098,6 +1104,7 @@
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1108,8 +1115,8 @@
/**
* Test checking non-null argument on {@link
- * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
- * File, TransportManager)}.
+ * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+ * File, File, TransportManager)}.
*/
@Test
public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1117,6 +1124,7 @@
NullPointerException.class,
() ->
UserBackupManagerService.createAndInitializeService(
+ USER_ID,
mContext,
new Trampoline(mContext),
mBackupThread,
@@ -1127,7 +1135,7 @@
private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
- mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+ USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
}
private void setUpPowerManager(UserBackupManagerService backupManagerService) {
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 099127c..5e84ab0 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -134,7 +134,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowQueuedWork;
@@ -169,6 +168,7 @@
private static final PackageData PACKAGE_2 = keyValuePackage(2);
private static final String BACKUP_AGENT_SHARED_PREFS_SYNCHRONIZER_CLASS =
"android.app.backup.BackupAgent$SharedPrefsSynchronizer";
+ private static final int USER_ID = 10;
@Mock private TransportManager mTransportManager;
@Mock private DataChangedJournal mOldJournal;
@@ -187,7 +187,6 @@
private File mBaseStateDir;
private File mDataDir;
private Application mApplication;
- private ShadowApplication mShadowApplication;
private Looper mMainLooper;
private FrameworkShadowLooper mShadowMainLooper;
private Context mContext;
@@ -199,7 +198,6 @@
mTransport = backupTransport();
mApplication = RuntimeEnvironment.application;
- mShadowApplication = shadowOf(mApplication);
mContext = mApplication;
mMainLooper = Looper.getMainLooper();
@@ -224,7 +222,7 @@
setUpBinderCallerAndApplicationAsSystem(mApplication);
mBackupManagerService =
spy(createUserBackupManagerServiceAndRunTasks(
- mContext, mBaseStateDir, mDataDir, mTransportManager));
+ USER_ID, mContext, mBaseStateDir, mDataDir, mTransportManager));
setUpBackupManagerServiceBasics(
mBackupManagerService,
mApplication,
@@ -2438,7 +2436,7 @@
packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
PackageInfo packageInfo = getPackageInfo(packageData);
mShadowPackageManager.addPackage(packageInfo);
- mShadowApplication.sendBroadcast(getPackageAddedIntent(packageData));
+ mContext.sendBroadcast(getPackageAddedIntent(packageData));
// Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
mShadowBackupLooper.runToEndOfTasks();
BackupAgent backupAgent = spy(BackupAgent.class);
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 859392d..4009876 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -479,8 +479,11 @@
@Test
public void testRestorePackage_whenCallerIsNotPackageAndPermissionGranted() throws Exception {
- mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
- ShadowBinder.setCallingUid(1);
+ final int pid = 1211;
+ final int uid = 1;
+ mShadowApplication.grantPermissions(pid, uid, android.Manifest.permission.BACKUP);
+ ShadowBinder.setCallingPid(pid);
+ ShadowBinder.setCallingUid(uid);
setUpPackage(PACKAGE_1, /* uid */ 2);
when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
setUpTransport(mTransport);
@@ -494,8 +497,11 @@
@Test
public void testRestorePackage_whenCallerIsNotPackageAndPermissionDenied() throws Exception {
- mShadowApplication.denyPermissions(android.Manifest.permission.BACKUP);
- ShadowBinder.setCallingUid(1);
+ final int pid = 1211;
+ final int uid = 1;
+ mShadowApplication.denyPermissions(pid, uid, android.Manifest.permission.BACKUP);
+ ShadowBinder.setCallingPid(pid);
+ ShadowBinder.setCallingUid(uid);
setUpPackage(PACKAGE_1, /* uid */ 2);
when(mBackupManagerService.getAvailableRestoreToken(PACKAGE_1)).thenReturn(TOKEN_1);
setUpTransport(mTransport);
@@ -508,7 +514,11 @@
@Test
public void testRestorePackage_whenPackageNotFound() throws Exception {
- mShadowApplication.grantPermissions(android.Manifest.permission.BACKUP);
+ final int pid = 1211;
+ final int uid = 1;
+ mShadowApplication.grantPermissions(pid, uid, android.Manifest.permission.BACKUP);
+ ShadowBinder.setCallingPid(pid);
+ ShadowBinder.setCallingUid(uid);
setUpPackage(PACKAGE_1, /* uid */ 1);
setUpTransport(mTransport);
IRestoreSession restoreSession = createActiveRestoreSession(null, mTransport);
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 06f6d21..47abcc5 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -58,13 +58,17 @@
* <p>If the class-under-test is going to execute methods as the system, it's a good idea to
* also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
*
- * @see #createUserBackupManagerServiceAndRunTasks(Context, HandlerThread, File, File,
+ * @see #createUserBackupManagerServiceAndRunTasks(int, Context, HandlerThread, File, File,
* TransportManager)
*/
public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
- Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
+ int userId,
+ Context context,
+ File baseStateDir,
+ File dataDir,
+ TransportManager transportManager) {
return createUserBackupManagerServiceAndRunTasks(
- context, startBackupThread(null), baseStateDir, dataDir, transportManager);
+ userId, context, startBackupThread(null), baseStateDir, dataDir, transportManager);
}
/**
@@ -75,6 +79,7 @@
* also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
*/
public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
+ int userId,
Context context,
HandlerThread backupThread,
File baseStateDir,
@@ -82,6 +87,7 @@
TransportManager transportManager) {
UserBackupManagerService backupManagerService =
UserBackupManagerService.createAndInitializeService(
+ userId,
context,
new Trampoline(context),
backupThread,
@@ -130,11 +136,13 @@
}
public static void setUpBinderCallerAndApplicationAsSystem(Application application) {
- ShadowBinder.setCallingUid(Process.SYSTEM_UID);
- ShadowBinder.setCallingPid(1211);
+ final int uid = Process.SYSTEM_UID;
+ final int pid = 1211;
+ ShadowBinder.setCallingUid(uid);
+ ShadowBinder.setCallingPid(pid);
ShadowApplication shadowApplication = shadowOf(application);
- shadowApplication.grantPermissions("android.permission.BACKUP");
- shadowApplication.grantPermissions("android.permission.CONFIRM_FULL_BACKUP");
+ shadowApplication.grantPermissions(pid, uid, "android.permission.BACKUP");
+ shadowApplication.grantPermissions(pid, uid, "android.permission.CONFIRM_FULL_BACKUP");
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index cff0521..1a16e56 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -46,9 +46,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -56,6 +58,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.location.LocationManager;
import android.location.LocationProvider;
@@ -70,6 +73,7 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.deviceidle.ConstraintController;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -77,6 +81,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -89,6 +94,7 @@
private DeviceIdleController mDeviceIdleController;
private AnyMotionDetectorForTest mAnyMotionDetector;
private AppStateTrackerForTest mAppStateTracker;
+ private DeviceIdleController.Constants mConstants;
private InjectorForTest mInjector;
private MockitoSession mMockingSession;
@@ -97,7 +103,7 @@
@Mock
private ConnectivityService mConnectivityService;
@Mock
- private DeviceIdleController.Constants mConstants;
+ private ContentResolver mContentResolver;
@Mock
private IActivityManager mIActivityManager;
@Mock
@@ -108,10 +114,13 @@
private PowerManager.WakeLock mWakeLock;
@Mock
private PowerManagerInternal mPowerManagerInternal;
+ @Mock
+ private SensorManager mSensorManager;
class InjectorForTest extends DeviceIdleController.Injector {
ConnectivityService connectivityService;
LocationManager locationManager;
+ ConstraintController constraintController;
InjectorForTest(Context ctx) {
super(ctx);
@@ -139,37 +148,51 @@
}
@Override
- DeviceIdleController.Constants getConstants(DeviceIdleController controller,
- Handler handler,
- ContentResolver resolver) {
- return mConstants;
- }
-
- @Override
LocationManager getLocationManager() {
return locationManager;
}
@Override
DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
- return mock(DeviceIdleController.MyHandler.class);
+ return mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
}
@Override
PowerManager getPowerManager() {
return mPowerManager;
}
+
+ @Override
+ SensorManager getSensorManager() {
+ return mSensorManager;
+ }
+
+ @Override
+ ConstraintController getConstraintController(
+ Handler handler, DeviceIdleController.LocalService localService) {
+ return constraintController;
+ }
+
+ @Override
+ boolean useMotionSensor() {
+ return true;
+ }
}
private class AnyMotionDetectorForTest extends AnyMotionDetector {
boolean isMonitoring = false;
AnyMotionDetectorForTest() {
- super(mPowerManager, mock(Handler.class), mock(SensorManager.class),
+ super(mPowerManager, mock(Handler.class), mSensorManager,
mock(DeviceIdleCallback.class), 0.5f);
}
@Override
+ public boolean hasSensor() {
+ return true;
+ }
+
+ @Override
public void checkForAnyMotion() {
isMonitoring = true;
}
@@ -201,7 +224,7 @@
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
- .mockStatic(LocalServices.class)
+ .spyStatic(LocalServices.class)
.startMocking();
spyOn(getContext());
doReturn(null).when(getContext()).registerReceiver(any(), any());
@@ -218,9 +241,13 @@
when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
doNothing().when(mWakeLock).acquire();
doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
+ doReturn(mock(Sensor.class)).when(mSensorManager)
+ .getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true));
+ doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
mInjector = new InjectorForTest(getContext());
+ doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
spyOn(mDeviceIdleController);
doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
@@ -228,6 +255,10 @@
mDeviceIdleController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mDeviceIdleController.setDeepEnabledForTest(true);
mDeviceIdleController.setLightEnabledForTest(true);
+
+ // Get the same Constants object that mDeviceIdleController got.
+ mConstants = mInjector.getConstants(mDeviceIdleController,
+ mInjector.getHandler(mDeviceIdleController), mContentResolver);
}
@After
@@ -235,9 +266,10 @@
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
- // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove
- // it after each test, otherwise, subsequent tests will fail.
+ // DeviceIdleController adds these to LocalServices in the constructor, so we have to remove
+ // them after each test, otherwise, subsequent tests will fail.
LocalServices.removeServiceForTest(AppStateTracker.class);
+ LocalServices.removeServiceForTest(DeviceIdleController.LocalService.class);
}
@Test
@@ -1456,8 +1488,8 @@
private void setAlarmSoon(boolean isSoon) {
if (isSoon) {
- doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
- mAlarmManager).getNextWakeFromIdleTime();
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2)
+ .when(mAlarmManager).getNextWakeFromIdleTime();
} else {
doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
}
@@ -1481,27 +1513,36 @@
assertFalse(mDeviceIdleController.isScreenOn());
break;
case STATE_IDLE_PENDING:
- assertTrue(mDeviceIdleController.mMotionListener.isActive());
+ assertEquals(
+ mDeviceIdleController.hasMotionSensor(),
+ mDeviceIdleController.mMotionListener.isActive());
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
assertFalse(mDeviceIdleController.isScreenOn());
break;
case STATE_SENSING:
- assertTrue(mDeviceIdleController.mMotionListener.isActive());
- assertTrue(mAnyMotionDetector.isMonitoring);
+ assertEquals(
+ mDeviceIdleController.hasMotionSensor(),
+ mDeviceIdleController.mMotionListener.isActive());
+ assertEquals(
+ mDeviceIdleController.hasMotionSensor(),
+ mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
assertFalse(mDeviceIdleController.isScreenOn());
break;
case STATE_LOCATING:
- assertTrue(mDeviceIdleController.mMotionListener.isActive());
- assertTrue(mAnyMotionDetector.isMonitoring);
+ assertEquals(
+ mDeviceIdleController.hasMotionSensor(),
+ mDeviceIdleController.mMotionListener.isActive());
assertFalse(mDeviceIdleController.isCharging());
assertFalse(mDeviceIdleController.isScreenOn());
break;
case STATE_IDLE:
- assertTrue(mDeviceIdleController.mMotionListener.isActive()
+ if (mDeviceIdleController.hasMotionSensor()) {
+ assertTrue(mDeviceIdleController.mMotionListener.isActive()
// If quick doze is enabled, the motion listener should NOT be active.
|| mDeviceIdleController.isQuickDozeEnabled());
+ }
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
assertFalse(mDeviceIdleController.isScreenOn());
@@ -1509,9 +1550,11 @@
verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
break;
case STATE_IDLE_MAINTENANCE:
- assertTrue(mDeviceIdleController.mMotionListener.isActive()
+ if (mDeviceIdleController.hasMotionSensor()) {
+ assertTrue(mDeviceIdleController.mMotionListener.isActive()
// If quick doze is enabled, the motion listener should NOT be active.
|| mDeviceIdleController.isQuickDozeEnabled());
+ }
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
assertFalse(mDeviceIdleController.isScreenOn());
diff --git a/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
new file mode 100644
index 0000000..f74ac1f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.deviceidle;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+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.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.DeviceIdleController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link com.android.server.deviceidle.BluetoothConstraint}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BluetoothConstraintTest {
+
+ private MockitoSession mMockingSession;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Handler mHandler;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private BluetoothManager mBluetoothManager;
+
+ @Mock
+ private DeviceIdleController.LocalService mDeviceIdleService;
+
+ private BluetoothConstraint mConstraint;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ doReturn(mBluetoothManager)
+ .when(mContext).getSystemService(BluetoothManager.class);
+ mConstraint = new BluetoothConstraint(mContext, mHandler, mDeviceIdleService);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ @Test
+ public void testIsConnected_noBluetoothAdapter() {
+ doReturn(null).when(mBluetoothManager).getAdapter();
+ assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+ }
+
+ @Test
+ public void testIsConnected_noConnectedDevice() {
+ enableBluetooth(true);
+ assertFalse(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+ }
+
+ @Test
+ public void testIsConnected_twoConnectedDevices() {
+ enableBluetooth(true, mock(BluetoothDevice.class), mock(BluetoothDevice.class));
+ assertTrue(BluetoothConstraint.isBluetoothConnected(mBluetoothManager));
+ }
+
+ @Test
+ public void testStartMonitoring_updatesActiveAtCorrectTimes() {
+ // First setup -> no callbacks should fire.
+ BluetoothConstraint constraint = mConstraint;
+ verify(mDeviceIdleService, never()).onConstraintStateChanged(any(), anyBoolean());
+ verify(mContext, never()).registerReceiver(eq(constraint.mReceiver), any());
+
+ InOrder order = inOrder(mDeviceIdleService);
+
+ // No devices -> active=false should be triggered.
+ enableBluetooth(true);
+ constraint.startMonitoring();
+ order.verify(mDeviceIdleService, times(1)).onConstraintStateChanged(any(), eq(false));
+
+ // One device -> active=true should be triggered.
+ enableBluetooth(true, mock(BluetoothDevice.class));
+ constraint.mReceiver.onReceive(
+ mContext, new Intent(BluetoothDevice.ACTION_ACL_CONNECTED));
+ constraint.startMonitoring();
+ order.verify(mDeviceIdleService, times(1)).exitIdle(eq("bluetooth"));
+
+ // Stop monitoring -> broadcast receiver should be unregistered.
+ constraint.stopMonitoring();
+ verify(mContext, times(1)).unregisterReceiver(eq(constraint.mReceiver));
+ order.verifyNoMoreInteractions();
+
+ }
+
+ private void enableBluetooth(boolean enabled, BluetoothDevice... devices) {
+ when(mBluetoothManager.getAdapter().isEnabled()).thenReturn(enabled);
+ when(mBluetoothManager.getConnectedDevices(eq(BluetoothProfile.GATT)))
+ .thenReturn(Arrays.asList(devices));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
index 52f434d..edd89f9 100644
--- a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
@@ -52,8 +52,8 @@
// Try to start watching noted ops
final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
try {
- appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
- AppOpsManager.OPSTR_RECORD_AUDIO}, listener);
+ appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_RECORD_AUDIO}, listener);
fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS);
} catch (SecurityException expected) {
/*ignored*/
@@ -67,23 +67,23 @@
// Start watching noted ops
final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
- appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
- AppOpsManager.OPSTR_CAMERA}, listener);
+ appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_CAMERA}, listener);
// Note some ops
- appOpsManager.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, Process.myUid(),
+ appOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION, Process.myUid(),
getContext().getPackageName());
- appOpsManager.noteOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(),
+ appOpsManager.noteOp(AppOpsManager.OP_CAMERA, Process.myUid(),
getContext().getPackageName());
// Verify that we got called for the ops being noted
final InOrder inOrder = inOrder(listener);
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
- .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION),
+ .times(1)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION),
eq(Process.myUid()), eq(getContext().getPackageName()),
eq(AppOpsManager.MODE_ALLOWED));
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
- .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_CAMERA),
+ .times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA),
eq(Process.myUid()), eq(getContext().getPackageName()),
eq(AppOpsManager.MODE_ALLOWED));
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 ff31435..db83505 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -23,6 +23,7 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -132,9 +133,19 @@
}
@Test
+ public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ mTrampoline.initializeService();
+
+ mTrampoline.unlockUser(UserHandle.USER_SYSTEM);
+
+ verify(mBackupManagerServiceMock).startServiceForUser(UserHandle.USER_SYSTEM);
+ }
+
+ @Test
public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.unlockUser(10);
@@ -144,7 +155,7 @@
@Test
public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.unlockUser(10);
@@ -152,27 +163,48 @@
}
@Test
- public void initializeService_forUserSystem_successfullyInitialized() {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ mTrampoline.initializeService();
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ mTrampoline.stopUser(UserHandle.USER_SYSTEM);
+
+ verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM);
}
- // The BackupManagerService can only be initialized by USER_SYSTEM, so we check that if any
- // other user trying to initialize it leaves it non-active.
@Test
- public void initializeService_forNonUserSystem_nonInitialized() {
- mTrampoline.initializeService(NON_USER_SYSTEM);
+ public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ mTrampoline.initializeService();
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ mTrampoline.stopUser(10);
+
+ verify(mBackupManagerServiceMock, never()).stopServiceForUser(10);
+ }
+
+ @Test
+ public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+ mTrampoline.initializeService();
+
+ mTrampoline.stopUser(10);
+
+ verify(mBackupManagerServiceMock).stopServiceForUser(10);
+ }
+
+ @Test
+ public void initializeService_successfullyInitializesBackupService() {
+ mTrampoline.initializeService();
+
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
public void initializeService_globallyDisabled_nonInitialized() {
TrampolineTestable.sBackupDisabled = true;
-
TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
- trampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ trampoline.initializeService();
assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@@ -181,14 +213,21 @@
@Test
public void initializeService_suppressFileExists_nonInitialized() {
when(mSuppressFileMock.exists()).thenReturn(true);
-
TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
- trampoline.initializeService(UserHandle.USER_SYSTEM);
+
+ trampoline.initializeService();
assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@Test
+ public void initializeService_doesNotStartServiceForUsers() {
+ mTrampoline.initializeService();
+
+ verify(mBackupManagerServiceMock, never()).startServiceForUser(anyInt());
+ }
+
+ @Test
public void isBackupServiceActive_calledBeforeInitialize_returnsFalse() {
assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
@@ -263,7 +302,7 @@
@Test
public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
throws IOException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -277,7 +316,7 @@
setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated_ioExceptionHandled()
throws IOException {
when(mSuppressFileMock.createNewFile()).thenThrow(new IOException());
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -302,10 +341,22 @@
}
@Test
+ public void dataChangedForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
+ }
+
+ @Test
public void dataChanged_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.dataChanged(PACKAGE_NAME);
- verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
}
@Test
@@ -315,10 +366,22 @@
}
@Test
+ public void clearBackupDataForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
+ }
+
+ @Test
public void clearBackupData_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
- verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
}
@Test
@@ -328,10 +391,22 @@
}
@Test
+ public void agentConnectedForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);
+
+ verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
+ }
+
+ @Test
public void agentConnected_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
- verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock);
+
+ verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
}
@Test
@@ -341,10 +416,22 @@
}
@Test
+ public void agentDisconnectedForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
+ }
+
+ @Test
public void agentDisconnected_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.agentDisconnected(PACKAGE_NAME);
- verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME);
+
+ verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
}
@Test
@@ -354,10 +441,22 @@
}
@Test
+ public void restoreAtInstallForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
+
+ verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
+ }
+
+ @Test
public void restoreAtInstall_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
- verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123);
+
+ verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
}
@Test
@@ -368,7 +467,7 @@
@Test
public void setBackupEnabledForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.setBackupEnabledForUser(mUserId, true);
@@ -378,7 +477,7 @@
@Test
public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.setBackupEnabled(true);
@@ -392,23 +491,22 @@
}
@Test
+ public void setAutoRestoreForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.setAutoRestoreForUser(mUserId, true);
+
+ verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
+ }
+
+ @Test
public void setAutoRestore_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.setAutoRestore(true);
- verify(mBackupManagerServiceMock).setAutoRestore(true);
- }
- @Test
- public void setBackupProvisioned_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.setBackupProvisioned(true);
- verifyNoMoreInteractions(mBackupManagerServiceMock);
- }
-
- @Test
- public void setBackupProvisioned_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.setBackupProvisioned(true);
- verify(mBackupManagerServiceMock).setBackupProvisioned(true);
+ verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
}
@Test
@@ -419,7 +517,7 @@
@Test
public void isBackupEnabledForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.isBackupEnabledForUser(mUserId);
@@ -429,7 +527,7 @@
@Test
public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.isBackupEnabled();
@@ -444,7 +542,7 @@
@Test
public void setBackupPassword_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
verify(mBackupManagerServiceMock).setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
}
@@ -457,7 +555,7 @@
@Test
public void hasBackupPassword_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.hasBackupPassword();
verify(mBackupManagerServiceMock).hasBackupPassword();
}
@@ -470,7 +568,7 @@
@Test
public void backupNowForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.backupNowForUser(mUserId);
@@ -480,7 +578,7 @@
@Test
public void backupNow_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.backupNow();
@@ -497,7 +595,7 @@
@Test
public void adbBackup_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
true, true, true, true, true, true,
PACKAGE_NAMES);
@@ -507,15 +605,17 @@
@Test
public void fullTransportBackup_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.fullTransportBackup(PACKAGE_NAMES);
+ mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void fullTransportBackup_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.fullTransportBackup(PACKAGE_NAMES);
- verify(mBackupManagerServiceMock).fullTransportBackup(PACKAGE_NAMES);
+ public void fullTransportBackupForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
+
+ verify(mBackupManagerServiceMock).fullTransportBackup(mUserId, PACKAGE_NAMES);
}
@Test
@@ -526,7 +626,7 @@
@Test
public void adbRestore_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock);
}
@@ -540,12 +640,43 @@
}
@Test
+ public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.acknowledgeFullBackupOrRestoreForUser(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
+
+ verify(mBackupManagerServiceMock)
+ .acknowledgeAdbBackupOrRestore(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
+ }
+
+ @Test
public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
mFullBackupRestoreObserverMock);
- verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD,
- ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock);
+
+ verify(mBackupManagerServiceMock)
+ .acknowledgeAdbBackupOrRestore(
+ mUserId,
+ 123,
+ true,
+ CURRENT_PASSWORD,
+ ENCRYPTION_PASSWORD,
+ mFullBackupRestoreObserverMock);
}
@Test
@@ -555,13 +686,22 @@
}
@Test
- public void getCurrentTransport_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
+ public void getCurrentTransportForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId));
+ verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
+ }
+
+ @Test
+ public void getCurrentTransport_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
+ mTrampoline.initializeService();
assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport());
- verify(mBackupManagerServiceMock).getCurrentTransport();
+ verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
}
@Test
@@ -571,28 +711,40 @@
}
@Test
- public void listAllTransports_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
+ public void listAllTransportsForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId));
+ verify(mBackupManagerServiceMock).listAllTransports(mUserId);
+ }
+
+
+ @Test
+ public void listAllTransports_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
+ mTrampoline.initializeService();
+
assertEquals(TRANSPORTS, mTrampoline.listAllTransports());
- verify(mBackupManagerServiceMock).listAllTransports();
+ verify(mBackupManagerServiceMock).listAllTransports(mUserId);
}
@Test
- public void listAllTransportComponents_calledBeforeInitialize_ignored() throws RemoteException {
- assertNull(mTrampoline.listAllTransportComponents());
+ public void listAllTransportComponentsForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ assertNull(mTrampoline.listAllTransportComponentsForUser(mUserId));
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void listAllTransportComponents_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.listAllTransportComponents()).thenReturn(
+ public void listAllTransportComponentsForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn(
TRANSPORT_COMPONENTS);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponents());
- verify(mBackupManagerServiceMock).listAllTransportComponents();
+ assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId));
+ verify(mBackupManagerServiceMock).listAllTransportComponents(mUserId);
}
@Test
@@ -604,28 +756,50 @@
@Test
public void getTransportWhitelist_forwarded() throws RemoteException {
when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
assertEquals(TRANSPORTS, mTrampoline.getTransportWhitelist());
verify(mBackupManagerServiceMock).getTransportWhitelist();
}
@Test
- public void describeTransport_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
- "Transport Destination", null, "Data Management");
+ public void updateTransportAttributesForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ mTrampoline.updateTransportAttributesForUser(
+ mUserId,
+ TRANSPORT_COMPONENT_NAME,
+ TRANSPORT_NAME,
+ null,
+ "Transport Destination",
+ null,
+ "Data Management");
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void describeTransport_forwarded() throws RemoteException {
+ public void updateTransportAttributesForUser_forwarded() throws RemoteException {
when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
- "Transport Destination", null, "Data Management");
- verify(mBackupManagerServiceMock).updateTransportAttributes(TRANSPORT_COMPONENT_NAME,
- TRANSPORT_NAME, null, "Transport Destination", null, "Data Management");
+ mTrampoline.updateTransportAttributesForUser(
+ mUserId,
+ TRANSPORT_COMPONENT_NAME,
+ TRANSPORT_NAME,
+ null,
+ "Transport Destination",
+ null,
+ "Data Management");
+
+ verify(mBackupManagerServiceMock)
+ .updateTransportAttributes(
+ mUserId,
+ TRANSPORT_COMPONENT_NAME,
+ TRANSPORT_NAME,
+ null,
+ "Transport Destination",
+ null,
+ "Data Management");
}
@Test
@@ -635,16 +809,31 @@
}
@Test
- public void selectBackupTransport_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.selectBackupTransport(TRANSPORT_NAME);
- verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME);
+ public void selectBackupTransportForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME);
+
+ verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
}
@Test
- public void selectBackupTransportAsync_calledBeforeInitialize_ignored() throws Exception {
+ public void selectBackupTransport_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
+ mTrampoline.selectBackupTransport(TRANSPORT_NAME);
+
+ verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
+ public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored()
+ throws Exception {
LinkedBlockingQueue<Integer> q = new LinkedBlockingQueue();
- mTrampoline.selectBackupTransportAsync(
+
+ mTrampoline.selectBackupTransportAsyncForUser(
+ mUserId,
TRANSPORT_COMPONENT_NAME,
new ISelectBackupTransportCallback() {
@Override
@@ -662,6 +851,7 @@
return null;
}
});
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
Integer errorCode = q.poll(5, TimeUnit.SECONDS);
assertNotNull(errorCode);
@@ -669,17 +859,19 @@
}
@Test
- public void selectBackupTransportAsync_calledBeforeInitialize_ignored_nullListener()
+ public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_nullListener()
throws Exception {
- mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
+ mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
// No crash.
}
@Test
- public void selectBackupTransportAsync_calledBeforeInitialize_ignored_listenerThrowException()
+ public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_listenerThrows()
throws Exception {
- mTrampoline.selectBackupTransportAsync(
+ mTrampoline.selectBackupTransportAsyncForUser(
+ mUserId,
TRANSPORT_COMPONENT_NAME,
new ISelectBackupTransportCallback() {
@Override
@@ -697,16 +889,19 @@
return null;
}
});
+
verifyNoMoreInteractions(mBackupManagerServiceMock);
// No crash.
}
@Test
- public void selectBackupTransportAsync_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
- verify(mBackupManagerServiceMock).selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME,
- null);
+ public void selectBackupTransportAsyncForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
+ verify(mBackupManagerServiceMock)
+ .selectBackupTransportAsync(mUserId, TRANSPORT_COMPONENT_NAME, null);
}
@Test
@@ -716,14 +911,28 @@
}
@Test
- public void getConfigurationIntent_forwarded() throws RemoteException {
+ public void getConfigurationIntentForUser_forwarded() throws RemoteException {
Intent configurationIntentStub = new Intent();
- when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
configurationIntentStub);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(
+ configurationIntentStub,
+ mTrampoline.getConfigurationIntentForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
+ public void getConfigurationIntent_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ Intent configurationIntentStub = new Intent();
+ when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
+ configurationIntentStub);
+ mTrampoline.initializeService();
+
assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
}
@Test
@@ -733,13 +942,26 @@
}
@Test
+ public void getDestinationStringForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
+ DESTINATION_STRING);
+ mTrampoline.initializeService();
+
+ assertEquals(
+ DESTINATION_STRING,
+ mTrampoline.getDestinationStringForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
public void getDestinationString_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
DESTINATION_STRING);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(DESTINATION_STRING, mTrampoline.getDestinationString(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
}
@Test
@@ -749,14 +971,28 @@
}
@Test
- public void getDataManagementIntent_forwarded() throws RemoteException {
+ public void getDataManagementIntentForUser_forwarded() throws RemoteException {
Intent dataManagementIntent = new Intent();
- when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
+ when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
dataManagementIntent);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(
+ dataManagementIntent,
+ mTrampoline.getDataManagementIntentForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
+ public void getDataManagementIntent_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ Intent dataManagementIntent = new Intent();
+ when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
+ dataManagementIntent);
+ mTrampoline.initializeService();
+
assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
}
@Test
@@ -766,26 +1002,42 @@
}
@Test
- public void getDataManagementLabel_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
+ public void getDataManagementLabelForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
DATA_MANAGEMENT_LABEL);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ assertEquals(
+ DATA_MANAGEMENT_LABEL,
+ mTrampoline.getDataManagementLabelForUser(mUserId, TRANSPORT_NAME));
+ verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
+ }
+
+ @Test
+ public void getDataManagementLabel_forwarded() throws RemoteException {
+ TrampolineTestable.sCallingUserId = mUserId;
+ when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
+ DATA_MANAGEMENT_LABEL);
+ mTrampoline.initializeService();
+
assertEquals(DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabel(TRANSPORT_NAME));
- verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME);
+ verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
}
@Test
public void beginRestoreSession_calledBeforeInitialize_ignored() throws RemoteException {
- mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+ mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void beginRestoreSession_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
- verify(mBackupManagerServiceMock).beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+ public void beginRestoreSessionForUser_forwarded() throws RemoteException {
+ mTrampoline.initializeService();
+
+ mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
+
+ verify(mBackupManagerServiceMock)
+ .beginRestoreSession(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
}
@Test
@@ -796,39 +1048,46 @@
@Test
public void opComplete_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ TrampolineTestable.sCallingUserId = mUserId;
+ mTrampoline.initializeService();
+
mTrampoline.opComplete(1, 2);
- verify(mBackupManagerServiceMock).opComplete(1, 2);
+
+ verify(mBackupManagerServiceMock).opComplete(mUserId, 1, 2);
}
@Test
- public void getAvailableRestoreToken_calledBeforeInitialize_ignored() throws RemoteException {
- assertEquals(0, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
+ public void getAvailableRestoreTokenForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ assertEquals(0, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void getAvailableRestoreToken_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.getAvailableRestoreToken(PACKAGE_NAME)).thenReturn(123L);
+ public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))
+ .thenReturn(123L);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertEquals(123, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
- verify(mBackupManagerServiceMock).getAvailableRestoreToken(PACKAGE_NAME);
+ assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
+ verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME);
}
@Test
- public void isAppEligibleForBackup_calledBeforeInitialize_ignored() throws RemoteException {
- assertFalse(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
+ public void isAppEligibleForBackupForUser_calledBeforeInitialize_ignored()
+ throws RemoteException {
+ assertFalse(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
verifyNoMoreInteractions(mBackupManagerServiceMock);
}
@Test
- public void isAppEligibleForBackup_forwarded() throws RemoteException {
- when(mBackupManagerServiceMock.isAppEligibleForBackup(PACKAGE_NAME)).thenReturn(true);
+ public void isAppEligibleForBackupForUser_forwarded() throws RemoteException {
+ when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME))
+ .thenReturn(true);
+ mTrampoline.initializeService();
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- assertTrue(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
- verify(mBackupManagerServiceMock).isAppEligibleForBackup(PACKAGE_NAME);
+ assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
+ verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME);
}
@Test
@@ -842,7 +1101,7 @@
public void requestBackupForUser_forwarded() throws RemoteException {
when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES,
mBackupObserverMock, mBackupManagerMonitorMock, 123));
@@ -855,7 +1114,7 @@
TrampolineTestable.sCallingUserId = mUserId;
when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES,
mBackupObserverMock, mBackupManagerMonitorMock, 123));
@@ -871,7 +1130,7 @@
@Test
public void cancelBackupsForUser_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.cancelBackupsForUser(mUserId);
@@ -881,7 +1140,7 @@
@Test
public void cancelBackups_forwardedToCallingUserId() throws RemoteException {
TrampolineTestable.sCallingUserId = mUserId;
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.cancelBackups();
@@ -899,7 +1158,7 @@
FullBackupJob fullBackupJob = new FullBackupJob();
when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
}
@@ -912,7 +1171,7 @@
@Test
public void endFullBackup_forwarded() throws RemoteException {
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.endFullBackup();
verify(mBackupManagerServiceMock).endFullBackup();
}
@@ -922,8 +1181,7 @@
when(mContextMock.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP)).thenReturn(
PackageManager.PERMISSION_DENIED);
-
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
@@ -946,8 +1204,7 @@
when(mContextMock.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP)).thenReturn(
PackageManager.PERMISSION_GRANTED);
-
- mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+ mTrampoline.initializeService();
mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 527a1ee..83c1c76 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -208,6 +208,7 @@
private static class TestableNotificationManagerService extends NotificationManagerService {
int countSystemChecks = 0;
boolean isSystemUid = true;
+ int countLogSmartSuggestionsVisible = 0;
public TestableNotificationManagerService(Context context) {
super(context);
@@ -231,11 +232,6 @@
}
@Override
- protected void reportSeen(NotificationRecord r) {
- return;
- }
-
- @Override
protected void reportUserInteraction(NotificationRecord r) {
return;
}
@@ -244,6 +240,14 @@
protected void handleSavePolicyFile() {
return;
}
+
+ @Override
+ void logSmartSuggestionsVisible(NotificationRecord r) {
+ super.logSmartSuggestionsVisible(r);
+ countLogSmartSuggestionsVisible++;
+ }
+
+
}
private class TestableToastCallback extends ITransientNotification.Stub {
@@ -3507,6 +3511,12 @@
}
@Test
+ public void testAppOverlay() throws Exception {
+ mBinderService.setAppOverlaysAllowed(PKG, mUid, false);
+ assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid));
+ }
+
+ @Test
public void testIsCallerInstantApp_primaryUser() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
@@ -3777,4 +3787,88 @@
verify(mAssistants).notifyAssistantActionClicked(
eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
}
+
+ @Test
+ public void testLogSmartSuggestionsVisible_triggerOnExpandAndVisible() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+ NotificationVisibility[] notificationVisibility = new NotificationVisibility[] {
+ NotificationVisibility.obtain(r.getKey(), 0, 0, true)
+ };
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility,
+ new NotificationVisibility[0]);
+
+ assertEquals(1, mService.countLogSmartSuggestionsVisible);
+ }
+
+ @Test
+ public void testLogSmartSuggestionsVisible_noTriggerOnExpand() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+
+ assertEquals(0, mService.countLogSmartSuggestionsVisible);
+ }
+
+ @Test
+ public void testLogSmartSuggestionsVisible_noTriggerOnVisible() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ NotificationVisibility[] notificationVisibility = new NotificationVisibility[]{
+ NotificationVisibility.obtain(r.getKey(), 0, 0, true)
+ };
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility,
+ new NotificationVisibility[0]);
+
+ assertEquals(0, mService.countLogSmartSuggestionsVisible);
+ }
+
+ public void testReportSeen_delegated() {
+ Notification.Builder nb =
+ new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, "opPkg", 0, "tag", mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.reportSeen(r);
+ verify(mAppUsageStats, never()).reportEvent(anyString(), anyInt(), anyInt());
+
+ }
+
+ @Test
+ public void testReportSeen_notDelegated() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+ mService.reportSeen(r);
+ verify(mAppUsageStats, times(1)).reportEvent(anyString(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void testNotificationStats_notificationError() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+ new Notification.Builder(mContext, mTestNotificationChannel.getId()).build(),
+ new UserHandle(mUid), null, 0);
+ NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ mService.addEnqueuedNotification(update);
+ assertNull(update.sbn.getNotification().getSmallIcon());
+
+ NotificationManagerService.PostNotificationRunnable runnable =
+ mService.new PostNotificationRunnable(update.getKey());
+ runnable.run();
+ waitForIdle();
+
+ ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
+ verify(mListeners).notifyRemovedLocked(any(), anyInt(), captor.capture());
+ assertNotNull(captor.getValue());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index b027935..0b73481 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -1584,39 +1584,6 @@
}
@Test
- public void testUpdateGroup_fromSystem_appOverlay() {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-
- // from system, allowed
- NotificationChannelGroup update = ncg.clone();
- update.setAllowAppOverlay(false);
-
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, update, false);
- NotificationChannelGroup updated =
- mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
- assertFalse(updated.canOverlayApps());
- assertEquals(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY,
- updated.getUserLockedFields());
- }
-
- @Test
- public void testUpdateGroup_fromApp_appOverlay() {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-
- // from app, not allowed
- NotificationChannelGroup update = new NotificationChannelGroup("group1", "name1");
- update.setAllowAppOverlay(false);
-
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
- NotificationChannelGroup updated =
- mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
- assertTrue(updated.canOverlayApps());
- assertEquals(0, updated.getUserLockedFields());
- }
-
- @Test
public void testCannotCreateChannel_badGroup() {
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -2192,4 +2159,32 @@
mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
}
+
+ @Test
+ public void testAllowAppOverlay_defaults() throws Exception {
+ assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testAllowAppOverlay_xml() throws Exception {
+ mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false);
+ assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+ assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+ mHelper.getAppLockedFields(PKG_O, UID_O));
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+ assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+ mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index b955e56..49f134f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -76,7 +76,7 @@
verify(mAm, times(1)).setExactAndAllowWhileIdle(
anyInt(), captor.capture(), any(PendingIntent.class));
long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime();
- assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 25);
+ assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 250);
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
}
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 dc3287e..a459b0a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -61,6 +61,7 @@
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.service.notification.ZenPolicy;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -683,6 +684,92 @@
}
@Test
+ public void testWriteXmlWithZenPolicy() throws Exception {
+ final String ruleId = "customRule";
+ setupZenConfig();
+
+ // one enabled automatic rule with zen policy
+ ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+ ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+ final ScheduleInfo customRuleInfo = new ScheduleInfo();
+ customRule.enabled = true;
+ customRule.creationTime = 0;
+ customRule.id = "customRule";
+ customRule.name = "Custom Rule";
+ customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.configurationActivity =
+ new ComponentName("android", "ScheduleConditionProvider");
+ customRule.pkg = customRule.configurationActivity.getPackageName();
+ customRule.zenPolicy = new ZenPolicy.Builder()
+ .allowAlarms(false)
+ .allowMedia(false)
+ .allowRepeatCallers(false)
+ .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS)
+ .allowEvents(true)
+ .allowReminders(false)
+ .build();
+ automaticRules.put("customRule", customRule);
+ mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+ ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(false, null);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, false);
+
+ ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
+ ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId);
+
+ assertEquals("Automatic rules mismatch", original, current);
+ }
+
+ @Test
+ public void testReadXmlRestoreWithZenPolicy() throws Exception {
+ final String ruleId = "customRule";
+ setupZenConfig();
+
+ // one enabled automatic rule with zen policy
+ ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+ ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+ final ScheduleInfo customRuleInfo = new ScheduleInfo();
+ customRule.enabled = true;
+ customRule.creationTime = 0;
+ customRule.id = ruleId;
+ customRule.name = "Custom Rule";
+ customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.configurationActivity =
+ new ComponentName("android", "ScheduleConditionProvider");
+ customRule.pkg = customRule.configurationActivity.getPackageName();
+ customRule.zenPolicy = new ZenPolicy.Builder()
+ .allowSystem(true)
+ .allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE)
+ .allowReminders(true)
+ .build();
+ automaticRules.put(ruleId, customRule);
+ mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+ ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(false, null);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mZenModeHelperSpy.readXml(parser, true);
+
+ ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
+ ZenModeConfig.ZenRule current = mZenModeHelperSpy.mConfig.automaticRules.get(ruleId);
+
+ assertEquals("Automatic rules mismatch", original, current);
+ }
+
+ @Test
public void testReadXmlRulesNotOverriden() throws Exception {
setupZenConfig();
@@ -896,6 +983,10 @@
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+ customRule.zenPolicy = new ZenPolicy.Builder()
+ .allowReminders(true)
+ .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+ .build();
automaticRules.put("customRule", customRule);
ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -943,6 +1034,10 @@
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
customRule.component = new ComponentName("android", "ScheduleConditionProvider");
+ customRule.zenPolicy = new ZenPolicy.Builder()
+ .allowReminders(true)
+ .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+ .build();
automaticRules.put("customRule", customRule);
ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -953,6 +1048,10 @@
defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
defaultScheduleRuleInfo);
defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
+ defaultScheduleRule.zenPolicy = new ZenPolicy.Builder()
+ .allowEvents(true)
+ .allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE)
+ .build();
automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule);
ZenModeConfig.ZenRule defaultEventRule = new ZenModeConfig.ZenRule();
@@ -963,6 +1062,11 @@
defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
defaultEventRuleInfo);
defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
+ defaultScheduleRule.zenPolicy = new ZenPolicy.Builder()
+ .allowAlarms(false)
+ .allowMedia(false)
+ .allowRepeatCallers(false)
+ .build();
automaticRules.put(ZenModeConfig.EVENTS_DEFAULT_RULE_ID, defaultEventRule);
mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 92b4dbb..bc62de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -155,15 +155,17 @@
// Set initial orientation and update.
mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
- null, mDisplayContent.getDisplayId());
+ mDisplayContent.updateOrientationFromAppTokens(
+ mDisplayContent.getRequestedOverrideConfiguration(),
+ null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
appWindow.mResizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
- mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
- null, mDisplayContent.getDisplayId());
+ mDisplayContent.updateOrientationFromAppTokens(
+ mDisplayContent.getRequestedOverrideConfiguration(),
+ null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
assertTrue(appWindow.mResizeReported);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 8430616..3826fac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -31,7 +31,13 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -60,9 +66,11 @@
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -475,6 +483,13 @@
@SuppressLint("InlinedApi")
public void testOrientationDefinedByKeyguard() {
final DisplayContent dc = createNewDisplay();
+
+ // When display content is created its configuration is not yet initialized, which could
+ // cause unnecessary configuration propagation, so initialize it here.
+ final Configuration config = new Configuration();
+ dc.computeScreenConfiguration(config);
+ dc.onRequestedOverrideConfigurationChanged(config);
+
// Create a window that requests landscape orientation. It will define device orientation
// by default.
final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
@@ -567,6 +582,52 @@
assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
}
+ @Test
+ public void testOnDescendantOrientationRequestChanged() {
+ final DisplayContent dc = createNewDisplay();
+ mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+ final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+ ? SCREEN_ORIENTATION_PORTRAIT
+ : SCREEN_ORIENTATION_LANDSCAPE;
+
+ final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+ window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+ window.mAppToken.setOrientation(newOrientation);
+
+ ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+ assertTrue("Display should rotate to handle orientation request by default.",
+ dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+
+ final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
+ verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
+ same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+ final Configuration newDisplayConfig = captor.getValue();
+ assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+ }
+
+ @Test
+ public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
+ final DisplayContent dc = createNewDisplay();
+ dc.getDisplayRotation().setFixedToUserRotation(true);
+ mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+ final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+ ? SCREEN_ORIENTATION_PORTRAIT
+ : SCREEN_ORIENTATION_LANDSCAPE;
+
+ final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+ window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+ window.mAppToken.setOrientation(newOrientation);
+
+ ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+ assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
+ + " user rotation.",
+ dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+ verify(mWm.mAtmService, never()).updateDisplayOverrideConfigurationLocked(any(),
+ eq(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index bf4b52e..6b31e6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -33,6 +33,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.ContentResolver;
@@ -234,7 +235,7 @@
}
@Test
- public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+ public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()
throws Exception {
mBuilder.build();
configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
@@ -604,6 +605,26 @@
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
+ // ========================
+ // Non-rotation API Tests
+ // ========================
+ @Test
+ public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+ mBuilder.build();
+
+ assertTrue("Display rotation should respect app requested orientation by"
+ + " default.", mTarget.respectAppRequestedOrientation());
+ }
+
+ @Test
+ public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+ mBuilder.build();
+ mTarget.setFixedToUserRotation(true);
+
+ assertFalse("Display rotation shouldn't respect app requested orientation if"
+ + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+ }
+
/**
* Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
* according to given parameters.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 9b18388..58302d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -23,8 +23,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -36,7 +36,7 @@
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
-import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -47,18 +47,27 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
import android.graphics.Rect;
-import android.os.Build;
import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
import androidx.test.filters.MediumTest;
+
+import com.android.internal.app.ResolverActivity;
+
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
+import java.util.List;
/**
* Tests for the {@link ActivityStackSupervisor} class.
@@ -385,31 +394,10 @@
}
/**
- * Tests home activities that targeted sdk before Q cannot start on secondary display.
- */
- @Test
- public void testStartHomeTargetSdkBeforeQ() throws Exception {
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
- mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
- doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
- final ActivityInfo info = new ActivityInfo();
- info.launchMode = LAUNCH_MULTIPLE;
- info.applicationInfo = new ApplicationInfo();
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
-
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
- assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
- }
-
- /**
* Tests that home activities can be started on the displays that supports system decorations.
*/
- @Test
- public void testStartHomeOnAllDisplays() {
+ // TODO (b/118206886): Will add it back once launcher's patch is merged into master.
+ private void testStartHomeOnAllDisplays() {
// Create secondary displays.
final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
@@ -477,4 +465,142 @@
assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
true /* allowInstrumenting*/));
}
+
+ /**
+ * Tests that secondary home should be selected if default home not set.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
+ final Intent defaultHomeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = ResolverActivity.class.getName();
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(defaultHomeIntent));
+
+ final String secondaryHomeComponent = mService.mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomeComponent);
+ final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+ final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+ final ActivityInfo aInfoSecondary = new ActivityInfo();
+ aInfoSecondary.name = comp.getClassName();
+ doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(secondaryHomeIntent));
+
+ // Should fallback to secondary home if default home not set.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ }
+
+ /**
+ * Tests that secondary home should be selected if default home not support secondary displays
+ * or there is no matched activity in the same package as selected default home.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
+ final Intent defaultHomeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(defaultHomeIntent));
+
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+ final String secondaryHomeComponent = mService.mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomeComponent);
+ final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
+ final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+ final ActivityInfo aInfoSecondary = new ActivityInfo();
+ aInfoSecondary.name = comp.getClassName();
+ doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(secondaryHomeIntent));
+
+ // Should fallback to secondary home if selected default home not support secondary displays
+ // or there is no matched activity in the same package as selected default home.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ }
+
+ /**
+ * Tests that default home activity should be selected if it already support secondary displays.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(homeIntent));
+
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ final ResolveInfo infoFake1 = new ResolveInfo();
+ infoFake1.activityInfo = new ActivityInfo();
+ infoFake1.activityInfo.name = "fakeActivity1";
+ infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+ final ResolveInfo infoFake2 = new ResolveInfo();
+ infoFake2.activityInfo = aInfoDefault;
+ resolutions.add(infoFake1);
+ resolutions.add(infoFake2);
+ doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+ doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
+
+ // Use default home activity if it support secondary displays.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(aInfoDefault.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ assertEquals(aInfoDefault.name, resolvedInfo.first.name);
+ }
+
+ /**
+ * Tests that the first one that matches should be selected if there are multiple activities.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(homeIntent));
+
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ final ResolveInfo infoFake1 = new ResolveInfo();
+ infoFake1.activityInfo = new ActivityInfo();
+ infoFake1.activityInfo.name = "fakeActivity1";
+ infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+ final ResolveInfo infoFake2 = new ResolveInfo();
+ infoFake2.activityInfo = new ActivityInfo();
+ infoFake2.activityInfo.name = "fakeActivity2";
+ infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
+ resolutions.add(infoFake1);
+ resolutions.add(infoFake2);
+ doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any());
+
+ doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
+
+ // Use the first one of matched activities in the same package as selected default home.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+
+ assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 83e7ee7..dfdbf32 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -49,12 +49,9 @@
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
import java.util.concurrent.CountDownLatch;
@@ -72,7 +69,6 @@
@Mock Transaction mMockTransaction;
@Mock AnimationSpec mMockAnimationSpec;
@Mock PowerManagerInternal mMockPowerManager;
- @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
private SurfaceAnimationRunner mSurfaceAnimationRunner;
private CountDownLatch mFinishCallbackLatch;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 3991e06..c343fe7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -20,6 +20,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertFalse;
@@ -66,6 +67,10 @@
synchronized (mWm.mGlobalLock) {
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
+
+ spyOn(mDisplayContent);
+ InputMonitor inputMonitor = mock(InputMonitor.class);
+ when(mDisplayContent.getInputMonitor()).thenReturn(inputMonitor);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 60f957f..e156143 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -38,8 +39,10 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -559,8 +562,7 @@
builder.setLayer(2).setIsVisible(true);
final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent =
visibleUnspecifiedRootChild.addChildWindow(builder);
- visibleUnspecifiedRootChildChildFillsParent.setOrientation(
- SCREEN_ORIENTATION_PORTRAIT);
+ visibleUnspecifiedRootChildChildFillsParent.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(SCREEN_ORIENTATION_PORTRAIT,
visibleUnspecifiedRootChildChildFillsParent.getOrientation());
assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
@@ -724,6 +726,19 @@
verify(grandChild, times(1)).onParentResize();
}
+ @Test
+ public void testOnDescendantOrientationRequestChangedPropagation() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+ final TestWindowContainer root = spy(builder.build());
+
+ final IBinder binder = mock(IBinder.class);
+ final ActivityRecord activityRecord = mock(ActivityRecord.class);
+ final TestWindowContainer child = root.addChildWindow();
+
+ child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord);
+ verify(root).onDescendantOrientationChanged(binder, activityRecord);
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 522ab9f..04e433e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -164,6 +164,7 @@
}
private void tearDown() {
+ cancelAllPendingAnimations();
waitUntilWindowManagerHandlersIdle();
destroyAllSurfaceTransactions();
destroyAllSurfaceControls();
@@ -178,6 +179,15 @@
return mService;
}
+ private void cancelAllPendingAnimations() {
+ for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
+ final SurfaceControl sc = reference.get();
+ if (sc != null) {
+ mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
+ }
+ }
+ }
+
void waitUntilWindowManagerHandlersIdle() {
final WindowManagerService wm = getWindowManagerService();
if (wm == null) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 60cb08f..294b750 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,12 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
import static com.android.internal.usb.DumpUtils.writeAccessory;
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
@@ -36,6 +42,7 @@
import android.content.res.Resources;
import android.debug.AdbManagerInternal;
import android.debug.IAdbTransport;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbConstants;
@@ -294,9 +301,10 @@
BroadcastReceiver portReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- UsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+ ParcelableUsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
UsbPortStatus status = intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS);
- mHandler.updateHostState(port, status);
+ mHandler.updateHostState(
+ port.getUsbPort(context.getSystemService(UsbManager.class)), status);
}
};
@@ -821,23 +829,20 @@
boolean prevHostConnected = mHostConnected;
UsbPort port = (UsbPort) args.arg1;
UsbPortStatus status = (UsbPortStatus) args.arg2;
- mHostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
- mSourcePower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE;
- mSinkPower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SINK;
- mAudioAccessoryConnected =
- (status.getCurrentMode() == UsbPort.MODE_AUDIO_ACCESSORY);
- mAudioAccessorySupported = port.isModeSupported(UsbPort.MODE_AUDIO_ACCESSORY);
+ mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+ mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+ mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+ mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+ mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
// Ideally we want to see if PR_SWAP and DR_SWAP is supported.
// But, this should be suffice, since, all four combinations are only supported
// when PR_SWAP and DR_SWAP are supported.
mSupportsAllCombinations = status.isRoleCombinationSupported(
- UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST)
- && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
- UsbPort.DATA_ROLE_HOST)
- && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE,
- UsbPort.DATA_ROLE_DEVICE)
- && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
- UsbPort.DATA_ROLE_HOST);
+ POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+ && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+ && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+ DATA_ROLE_DEVICE)
+ && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST);
args.recycle();
updateUsbNotification(false);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 96618f5..6f210e3 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,12 +16,22 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
import static com.android.internal.usb.DumpUtils.writePort;
import static com.android.internal.usb.DumpUtils.writePortStatus;
+import android.Manifest;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
@@ -78,13 +88,13 @@
// All non-trivial role combinations.
private static final int COMBO_SOURCE_HOST =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
- private static final int COMBO_SOURCE_DEVICE =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
+ UsbPort.combineRolesAsBit(POWER_ROLE_SOURCE, DATA_ROLE_HOST);
+ private static final int COMBO_SOURCE_DEVICE = UsbPort.combineRolesAsBit(
+ POWER_ROLE_SOURCE, DATA_ROLE_DEVICE);
private static final int COMBO_SINK_HOST =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
- private static final int COMBO_SINK_DEVICE =
- UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
+ UsbPort.combineRolesAsBit(POWER_ROLE_SINK, DATA_ROLE_HOST);
+ private static final int COMBO_SINK_DEVICE = UsbPort.combineRolesAsBit(
+ POWER_ROLE_SINK, DATA_ROLE_DEVICE);
// The system context.
private final Context mContext;
@@ -217,12 +227,12 @@
final int newMode;
if ((!canChangePowerRole && currentPowerRole != newPowerRole)
|| (!canChangeDataRole && currentDataRole != newDataRole)) {
- if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
- && newDataRole == UsbPort.DATA_ROLE_HOST) {
- newMode = UsbPort.MODE_DFP;
- } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
- && newDataRole == UsbPort.DATA_ROLE_DEVICE) {
- newMode = UsbPort.MODE_UFP;
+ if (canChangeMode && newPowerRole == POWER_ROLE_SOURCE
+ && newDataRole == DATA_ROLE_HOST) {
+ newMode = MODE_DFP;
+ } else if (canChangeMode && newPowerRole == POWER_ROLE_SINK
+ && newDataRole == DATA_ROLE_DEVICE) {
+ newMode = MODE_UFP;
} else {
logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
+ "while attempting to change role: " + portInfo
@@ -607,7 +617,7 @@
IndentingPrintWriter pw) {
// Only allow mode switch capability for dual role ports.
// Validate that the current mode matches the supported modes we expect.
- if ((supportedModes & UsbPort.MODE_DUAL) != UsbPort.MODE_DUAL) {
+ if ((supportedModes & MODE_DUAL) != MODE_DUAL) {
canChangeMode = false;
if (currentMode != 0 && currentMode != supportedModes) {
logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
@@ -633,16 +643,16 @@
// Can only change power role.
// Assume data role must remain at its current value.
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- UsbPort.POWER_ROLE_SOURCE, currentDataRole);
+ POWER_ROLE_SOURCE, currentDataRole);
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- UsbPort.POWER_ROLE_SINK, currentDataRole);
+ POWER_ROLE_SINK, currentDataRole);
} else if (canChangeDataRole) {
// Can only change data role.
// Assume power role must remain at its current value.
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- currentPowerRole, UsbPort.DATA_ROLE_HOST);
+ currentPowerRole, DATA_ROLE_HOST);
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
- currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
+ currentPowerRole, DATA_ROLE_DEVICE);
} else if (canChangeMode) {
// Can only change the mode.
// Assume both standard UFP and DFP configurations will become available
@@ -654,7 +664,8 @@
// Update the port data structures.
PortInfo portInfo = mPorts.get(portId);
if (portInfo == null) {
- portInfo = new PortInfo(portId, supportedModes);
+ portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId,
+ supportedModes);
portInfo.setStatus(currentMode, canChangeMode,
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
@@ -701,12 +712,13 @@
intent.addFlags(
Intent.FLAG_RECEIVER_FOREGROUND |
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
+ intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort));
intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
// Guard against possible reentrance by posting the broadcast from the handler
// instead of from within the critical section.
- mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL));
+ mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ Manifest.permission.MANAGE_USB));
// Log to statsd
if (!mConnected.containsKey(portInfo.mUsbPort.getId())
@@ -772,8 +784,8 @@
// 0 when port is connected. Else reports the last connected duration
public long mLastConnectDurationMillis;
- public PortInfo(String portId, int supportedModes) {
- mUsbPort = new UsbPort(portId, supportedModes);
+ PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes) {
+ mUsbPort = new UsbPort(usbManager, portId, supportedModes);
}
public boolean setStatus(int currentMode, boolean canChangeMode,
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index f9abedf..9115477 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,14 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
@@ -27,6 +35,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
+import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
@@ -52,7 +61,9 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
/**
* UsbService manages all USB related state, including both host and device support.
@@ -489,12 +500,25 @@
}
@Override
- public UsbPort[] getPorts() {
+ public List<ParcelableUsbPort> getPorts() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long ident = Binder.clearCallingIdentity();
try {
- return mPortManager != null ? mPortManager.getPorts() : null;
+ if (mPortManager == null) {
+ return null;
+ } else {
+ final UsbPort[] ports = mPortManager.getPorts();
+
+ final int numPorts = ports.length;
+ ArrayList<ParcelableUsbPort> parcelablePorts = new ArrayList<>();
+ for (int i = 0; i < numPorts; i++) {
+ parcelablePorts.add(ParcelableUsbPort.of(ports[i]));
+ }
+
+ return parcelablePorts;
+ }
+
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -588,10 +612,10 @@
final int powerRole;
switch (args[2]) {
case "source":
- powerRole = UsbPort.POWER_ROLE_SOURCE;
+ powerRole = POWER_ROLE_SOURCE;
break;
case "sink":
- powerRole = UsbPort.POWER_ROLE_SINK;
+ powerRole = POWER_ROLE_SINK;
break;
case "no-power":
powerRole = 0;
@@ -603,10 +627,10 @@
final int dataRole;
switch (args[3]) {
case "host":
- dataRole = UsbPort.DATA_ROLE_HOST;
+ dataRole = DATA_ROLE_HOST;
break;
case "device":
- dataRole = UsbPort.DATA_ROLE_DEVICE;
+ dataRole = DATA_ROLE_DEVICE;
break;
case "no-data":
dataRole = 0;
@@ -631,13 +655,13 @@
final int supportedModes;
switch (args[2]) {
case "ufp":
- supportedModes = UsbPort.MODE_UFP;
+ supportedModes = MODE_UFP;
break;
case "dfp":
- supportedModes = UsbPort.MODE_DFP;
+ supportedModes = MODE_DFP;
break;
case "dual":
- supportedModes = UsbPort.MODE_DUAL;
+ supportedModes = MODE_DUAL;
break;
case "none":
supportedModes = 0;
@@ -658,10 +682,10 @@
final boolean canChangeMode = args[2].endsWith("?");
switch (canChangeMode ? removeLastChar(args[2]) : args[2]) {
case "ufp":
- mode = UsbPort.MODE_UFP;
+ mode = MODE_UFP;
break;
case "dfp":
- mode = UsbPort.MODE_DFP;
+ mode = MODE_DFP;
break;
default:
pw.println("Invalid mode: " + args[2]);
@@ -671,10 +695,10 @@
final boolean canChangePowerRole = args[3].endsWith("?");
switch (canChangePowerRole ? removeLastChar(args[3]) : args[3]) {
case "source":
- powerRole = UsbPort.POWER_ROLE_SOURCE;
+ powerRole = POWER_ROLE_SOURCE;
break;
case "sink":
- powerRole = UsbPort.POWER_ROLE_SINK;
+ powerRole = POWER_ROLE_SINK;
break;
default:
pw.println("Invalid power role: " + args[3]);
@@ -684,10 +708,10 @@
final boolean canChangeDataRole = args[4].endsWith("?");
switch (canChangeDataRole ? removeLastChar(args[4]) : args[4]) {
case "host":
- dataRole = UsbPort.DATA_ROLE_HOST;
+ dataRole = DATA_ROLE_HOST;
break;
case "device":
- dataRole = UsbPort.DATA_ROLE_DEVICE;
+ dataRole = DATA_ROLE_DEVICE;
break;
default:
pw.println("Invalid data role: " + args[4]);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index d6b40ae..38efc74 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -622,6 +622,7 @@
onRecognitionFailureLocked();
break;
case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
+ case SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE:
if (isKeyphraseRecognitionEvent(event)) {
onKeyphraseRecognitionSuccessLocked((KeyphraseRecognitionEvent) event);
} else {
@@ -638,7 +639,8 @@
private void onGenericRecognitionSuccessLocked(GenericRecognitionEvent event) {
MetricsLogger.count(mContext, "sth_generic_recognition_event", 1);
- if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS) {
+ if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS
+ && event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
return;
}
ModelData model = getModelDataForLocked(event.soundModelHandle);
@@ -655,7 +657,9 @@
return;
}
- model.setStopped();
+ if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
+ model.setStopped();
+ }
try {
callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event);
} catch (DeadObjectException e) {
@@ -797,7 +801,10 @@
Slog.w(TAG, "Received onRecognition event without callback for keyphrase model.");
return;
}
- modelData.setStopped();
+
+ if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
+ modelData.setStopped();
+ }
try {
modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 06059c8..757d863 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -73,7 +73,7 @@
bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; }
private:
- TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
+ explicit TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
const std::string descriptor_;
};
@@ -83,7 +83,7 @@
class Prototype {
public:
template <typename... TypeDescriptors>
- Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
+ explicit Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
: return_type_{return_type}, param_types_{param_types...} {}
// Encode this prototype into the dex file.
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 9351dc3..55bfdc7 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -42,7 +42,7 @@
class ViewCompilerXmlVisitor : public XMLVisitor {
public:
- ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
+ explicit ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
bool VisitEnter(const XMLDocument& /*doc*/) override {
builder_->Start();
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
similarity index 73%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
index b7a7920..e2fa7e4 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2015, The Android Open Source Project
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package android.hardware.usb;
+package android.telecom;
-parcelable UsbPort;
+/**
+ * {@hide}
+ */
+parcelable PhoneAccountSuggestion;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
index 4e6a178..b401bcf 100644
--- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -24,6 +24,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
public final class PhoneAccountSuggestion implements Parcelable {
@@ -132,4 +133,19 @@
dest.writeInt(mReason);
dest.writeByte((byte) (mShouldAutoSelect ? 1 : 0));
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PhoneAccountSuggestion that = (PhoneAccountSuggestion) o;
+ return mReason == that.mReason
+ && mShouldAutoSelect == that.mShouldAutoSelect
+ && Objects.equals(mHandle, that.mHandle);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mHandle, mReason, mShouldAutoSelect);
+ }
}
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
new file mode 100644
index 0000000..ba3822c
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+import com.android.internal.telecom.IPhoneAccountSuggestionService;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for service that allows system apps to suggest phone accounts for outgoing calls.
+ *
+ * Phone account suggestions allow OEMs to intelligently select phone accounts based on knowledge
+ * about the user's past behavior, carrier billing patterns, or other factors unknown to the AOSP
+ * Telecom system.
+ * OEMs who wish to provide a phone account suggestion service on their device should implement this
+ * service in an app that resides in the /system/priv-app/ directory on their device. For security
+ * reasons, the service's entry {@code AndroidManifest.xml} file must declare the
+ * {@link android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE} permission:
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourServiceName"
+ * android:permission="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telecom.PhoneAccountSuggestionService"/>
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ * Only one system app on each device may implement this service. If multiple system apps implement
+ * this service, none of them will be queried for suggestions.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class PhoneAccountSuggestionService extends Service {
+ /**
+ * The {@link Intent} that must be declared in the {@code intent-filter} element of the
+ * service's manifest entry.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+
+ private IPhoneAccountSuggestionService mInterface = new IPhoneAccountSuggestionService.Stub() {
+ @Override
+ public void onAccountSuggestionRequest(IPhoneAccountSuggestionCallback callback,
+ String number) {
+ mCallbackMap.put(number, callback);
+ PhoneAccountSuggestionService.this.onAccountSuggestionRequest(number);
+ }
+ };
+
+ private final Map<String, IPhoneAccountSuggestionCallback> mCallbackMap =
+ new HashMap<>();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mInterface.asBinder();
+ }
+
+ /**
+ * The system calls this method during the outgoing call flow if it needs account suggestions.
+ *
+ * The implementer of this service must override this method to implement its account suggestion
+ * logic. After preparing the suggestions, the implementation of the service must call
+ * {@link #suggestPhoneAccounts(String, List)} to deliver the suggestions back to the system.
+ *
+ * Note that the system will suspend the outgoing call process after it calls this method until
+ * this service calls {@link #suggestPhoneAccounts}.
+ *
+ * @param number The phone number to provide suggestions for.
+ */
+ public void onAccountSuggestionRequest(@NonNull String number) {}
+
+ /**
+ * The implementation of this service calls this method to deliver suggestions to the system.
+ *
+ * The implementation of this service must call this method after receiving a call to
+ * {@link #onAccountSuggestionRequest(String)}. If no suggestions are available, pass an empty
+ * list as the {@code suggestions} argument.
+ *
+ * @param number The phone number to provide suggestions for.
+ * @param suggestions The list of suggestions.
+ */
+ public final void suggestPhoneAccounts(@NonNull String number,
+ @NonNull List<PhoneAccountSuggestion> suggestions) {
+ IPhoneAccountSuggestionCallback callback = mCallbackMap.remove(number);
+ if (callback == null) {
+ Log.w(this, "No suggestions requested for the number %s", Log.pii(number));
+ return;
+ }
+ try {
+ callback.suggestPhoneAccounts(number, suggestions);
+ } catch (RemoteException e) {
+ Log.w(this, "Remote exception calling suggestPhoneAccounts");
+ }
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
new file mode 100644
index 0000000..cb14241
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.internal.telecom;
+
+import android.telecom.PhoneAccountSuggestion;
+/**
+ * Internal remote callback interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionCallback{
+ void suggestPhoneAccounts(in String number, in List<PhoneAccountSuggestion> suggestions);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
new file mode 100644
index 0000000..0ffab93
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.internal.telecom;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+
+/**
+ * Internal remote interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionService {
+ void onAccountSuggestionRequest(in IPhoneAccountSuggestionCallback callback,
+ in String number);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 50204e7..954a709 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -292,6 +292,8 @@
void setTestDefaultCallRedirectionApp(String packageName);
+ void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
+
void setTestDefaultCallScreeningApp(String packageName);
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 1cbe5a2..c115a4b 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2732,10 +2732,26 @@
/**
* The {@code content://} style URL for this table.
+ * For MSIM, this will return APNs for the default subscription
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM,
+ * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id.
*/
public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
/**
+ * The {@code content://} style URL for this table. Used for APN query based on current
+ * subscription. Instead of specifying carrier matching information in the selection,
+ * this API will return all matching APNs from current subscription carrier and queries
+ * will be applied on top of that. If there is no match for MVNO (Mobile Virtual Network
+ * Operator) APNs, return APNs from its MNO (based on mccmnc) instead. For MSIM, this will
+ * return APNs for the default subscription
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId for MSIM,
+ * use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id.
+ */
+ public static final Uri SIM_APN_URI = Uri.parse(
+ "content://telephony/carriers/sim_apn_list");
+
+ /**
* The {@code content://} style URL to be called from DevicePolicyManagerService,
* can manage DPC-owned APNs.
* @hide
@@ -2745,7 +2761,9 @@
/**
* The {@code content://} style URL to be called from Telephony to query APNs.
* When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only
- * non-DPC-owned APNs are returned.
+ * non-DPC-owned APNs are returned. For MSIM, this will return APNs for the default
+ * subscription {@link SubscriptionManager#getDefaultSubscriptionId()}. To specify subId
+ * for MSIM, use {@link Uri#withAppendedPath(Uri, String)} to append with subscription id.
* @hide
*/
public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered");
@@ -2759,13 +2777,6 @@
"content://telephony/carriers/enforce_managed");
/**
- * The {@code content://} style URL to be called from Telephony to query current APNs.
- * @hide
- */
- public static final Uri SIM_APN_LIST = Uri.parse(
- "content://telephony/carriers/sim_apn_list");
-
- /**
* The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
* @hide
*/
@@ -2839,18 +2850,30 @@
/**
* Mobile Country Code (MCC).
* <P>Type: TEXT</P>
+ * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+ * matching APNs based on current subscription carrier, thus no need to specify MCC and
+ * other carrier matching information. In the future, Android will not support MCC for
+ * APN query.
*/
public static final String MCC = "mcc";
/**
* Mobile Network Code (MNC).
* <P>Type: TEXT</P>
+ * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+ * matching APNs based on current subscription carrier, thus no need to specify MNC and
+ * other carrier matching information. In the future, Android will not support MNC for
+ * APN query.
*/
public static final String MNC = "mnc";
/**
* Numeric operator ID (as String). Usually {@code MCC + MNC}.
* <P>Type: TEXT</P>
+ * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+ * matching APNs based on current subscription carrier, thus no need to specify Numeric
+ * and other carrier matching information. In the future, Android will not support Numeric
+ * for APN query.
*/
public static final String NUMERIC = "numeric";
@@ -2931,6 +2954,10 @@
* MVNO type:
* {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
* <P>Type: TEXT</P>
+ * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+ * matching APNs based on current subscription carrier, thus no need to specify MVNO_TYPE
+ * and other carrier matching information. In the future, Android will not support MVNO_TYPE
+ * for APN query.
*/
public static final String MVNO_TYPE = "mvno_type";
@@ -2943,6 +2970,10 @@
* <li>GID: 4E, 33, ...</li>
* </ul>
* <P>Type: TEXT</P>
+ * @deprecated Use {@link #SIM_APN_URI} to query APN instead, this API will return
+ * matching APNs based on current subscription carrier, thus no need to specify
+ * MVNO_MATCH_DATA and other carrier matching information. In the future, Android will not
+ * support MVNO_MATCH_DATA for APN query.
*/
public static final String MVNO_MATCH_DATA = "mvno_match_data";
@@ -3151,7 +3182,6 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface EditStatus {}
-
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fd14916..6e425de 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2000,6 +2000,8 @@
* Determine whether to use only RSRP for the number of LTE signal bars.
* @hide
*/
+ // FIXME: this key and related keys must not be exposed without a consistent philosophy for
+ // all RATs.
public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
"use_only_rsrp_for_lte_signal_bar_bool";
@@ -2243,6 +2245,8 @@
* Currently this only supports the value "rscp"
* @hide
*/
+ // FIXME: this key and related keys must not be exposed without a consistent philosophy for
+ // all RATs.
public static final String KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING =
"wcdma_default_signal_strength_measurement_string";
@@ -2400,6 +2404,34 @@
public static final String KEY_5G_ICON_CONFIGURATION_STRING =
"5g_icon_configuration_string";
+ /**
+ * Controls RSRP threshold at which AlternativeNetworkService will decide whether
+ * the opportunistic network is good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT =
+ "opportunistic_network_entry_threshold_rsrp_int";
+
+ /**
+ * Controls RSSNR threshold at which AlternativeNetworkService will decide whether
+ * the opportunistic network is good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT =
+ "opportunistic_network_entry_threshold_rssnr_int";
+
+ /**
+ * Controls RSRP threshold below which AlternativeNetworkService will decide whether
+ * the opportunistic network available is not good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT =
+ "opportunistic_network_exit_threshold_rsrp_int";
+
+ /**
+ * Controls RSSNR threshold below which AlternativeNetworkService will decide whether
+ * the opportunistic network available is not good enough for internet data.
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT =
+ "opportunistic_network_exit_threshold_rssnr_int";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -2761,6 +2793,14 @@
sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
+ /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
+ /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
+ /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45);
+ /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 04c28e5..c8a899b 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -169,6 +170,7 @@
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 04b6a6c..8e1877d 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -197,6 +198,7 @@
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 8b1c1b9..f77c468 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -116,6 +117,7 @@
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 3416ffe..31f9e6d 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -173,6 +174,7 @@
/**
* @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
*/
+ @Nullable
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index fd21d42..f2c14a1 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -16,6 +16,8 @@
package android.telephony;
+import android.os.PersistableBundle;
+
/**
* Abstract base class for cell phone signal strength related information.
*/
@@ -80,9 +82,74 @@
*/
public abstract CellSignalStrength copy();
+ /**
+ * Checks and returns whether there are any non-default values in this CellSignalStrength.
+ *
+ * Checks all the values in the subclass of CellSignalStrength and returns true if any of them
+ * have been set to a value other than their default.
+ *
+ * @hide
+ */
+ public abstract boolean isValid();
+
@Override
public abstract int hashCode();
@Override
public abstract boolean equals (Object o);
+
+ /**
+ * Calculate and set the carrier-influenced values such as the signal "Level".
+ *
+ * @hide
+ */
+ public abstract void updateLevel(PersistableBundle cc, ServiceState ss);
+
+ // Range for RSSI in ASU (0-31, 99) as defined in TS 27.007 8.69
+ /** @hide */
+ protected static final int getRssiDbmFromAsu(int asu) {
+ if (asu > 31 || asu < 0) return CellInfo.UNAVAILABLE;
+ return -113 + (2 * asu);
+ }
+
+ // Range for RSSI in ASU (0-31, 99) as defined in TS 27.007 8.69
+ /** @hide */
+ protected static final int getAsuFromRssiDbm(int dbm) {
+ if (dbm == CellInfo.UNAVAILABLE) return 99;
+ return (dbm / 2) + 113;
+ }
+
+ // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
+ /** @hide */
+ protected static final int getRscpDbmFromAsu(int asu) {
+ if (asu > 96 || asu < 0) return CellInfo.UNAVAILABLE;
+ return asu - 120;
+ }
+
+ // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
+ /** @hide */
+ protected static final int getAsuFromRscpDbm(int dbm) {
+ if (dbm == CellInfo.UNAVAILABLE) return 255;
+ return dbm + 120;
+ }
+
+ // Range for SNR in ASU (0-49, 255) as defined in TS 27.007 8.69
+ /** @hide */
+ protected static final int getEcNoDbFromAsu(int asu) {
+ if (asu > 49 || asu < 0) return CellInfo.UNAVAILABLE;
+ return -24 + (asu / 2);
+ }
+
+ /** @hide */
+ protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) {
+ if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE;
+ return value;
+ }
+
+ /** @hide */
+ protected static final int inRangeOrUnavailable(
+ int value, int rangeMin, int rangeMax, int special) {
+ if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
+ return value;
+ }
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 5123052..47faf1e 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.telephony.Rlog;
import java.util.Objects;
@@ -35,6 +36,7 @@
private int mEvdoDbm; // This value is the EVDO RSSI value
private int mEvdoEcio; // This value is the EVDO Ec/Io
private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
+ private int mLevel;
/** @hide */
public CellSignalStrengthCdma() {
@@ -55,23 +57,29 @@
* rather than left as -1, which is a departure from SignalStrength, which is stuck with the
* values it currently reports.
*
- * @param cdmaDbm negative of the CDMA signal strength value or -1 if invalid.
- * @param cdmaEcio negative of the CDMA pilot/noise ratio or -1 if invalid.
- * @param evdoDbm negative of the EvDO signal strength value or -1 if invalid.
- * @param evdoEcio negative of the EvDO pilot/noise ratio or -1 if invalid.
- * @param evdoSnr an SNR value 0..8 or -1 if invalid.
+ * @param cdmaDbm CDMA signal strength value or CellInfo.UNAVAILABLE if invalid.
+ * @param cdmaEcio CDMA pilot/noise ratio or CellInfo.UNAVAILABLE if invalid.
+ * @param evdoDbm negative of the EvDO signal strength value or CellInfo.UNAVAILABLE if invalid.
+ * @param evdoEcio negative of the EvDO pilot/noise ratio or CellInfo.UNAVAILABLE if invalid.
+ * @param evdoSnr an SNR value 0..8 or CellInfo.UNVAILABLE if invalid.
* @hide
*/
public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
int evdoSnr) {
- // The values here were lifted from SignalStrength.validateInput()
- // FIXME: Combine all checking and setting logic between this and SignalStrength.
- mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120)) ? -cdmaDbm : CellInfo.UNAVAILABLE;
- mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : CellInfo.UNAVAILABLE;
+ mCdmaDbm = inRangeOrUnavailable(cdmaDbm, -120, 0);
+ mCdmaEcio = inRangeOrUnavailable(cdmaEcio, -160, 0);
+ mEvdoDbm = inRangeOrUnavailable(evdoDbm, -120, 0);
+ mEvdoEcio = inRangeOrUnavailable(evdoEcio, -160, 0);
+ mEvdoSnr = inRangeOrUnavailable(evdoSnr, 0, 8);
- mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : CellInfo.UNAVAILABLE;
- mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : CellInfo.UNAVAILABLE;
- mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : CellInfo.UNAVAILABLE;
+ updateLevel(null, null);
+ }
+
+ /** @hide */
+ public CellSignalStrengthCdma(android.hardware.radio.V1_0.CdmaSignalStrength cdma,
+ android.hardware.radio.V1_0.EvdoSignalStrength evdo) {
+ // Convert from HAL values as part of construction.
+ this(-cdma.dbm, -cdma.ecio, -evdo.dbm, -evdo.ecio, evdo.signalNoiseRatio);
}
/** @hide */
@@ -86,6 +94,7 @@
mEvdoDbm = s.mEvdoDbm;
mEvdoEcio = s.mEvdoEcio;
mEvdoSnr = s.mEvdoSnr;
+ mLevel = s.mLevel;
}
/** @hide */
@@ -102,6 +111,7 @@
mEvdoDbm = CellInfo.UNAVAILABLE;
mEvdoEcio = CellInfo.UNAVAILABLE;
mEvdoSnr = CellInfo.UNAVAILABLE;
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
/**
@@ -112,26 +122,54 @@
*/
@Override
public int getLevel() {
- int level;
+ return mLevel;
+ }
+ /** @hide */
+ @Override
+ public void updateLevel(PersistableBundle cc, ServiceState ss) {
int cdmaLevel = getCdmaLevel();
int evdoLevel = getEvdoLevel();
if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
/* We don't know evdo, use cdma */
- level = getCdmaLevel();
+ mLevel = getCdmaLevel();
} else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
/* We don't know cdma, use evdo */
- level = getEvdoLevel();
+ mLevel = getEvdoLevel();
} else {
/* We know both, use the lowest level */
- level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
+ mLevel = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
}
- if (DBG) log("getLevel=" + level);
- return level;
}
/**
- * Get the signal level as an asu value between 0..97, 99 is unknown
+ * Get the 1xRTT Level in (Android) ASU.
+ *
+ * There is no standard definition of ASU for CDMA; however, Android defines it as the
+ * the lesser of the following two results (for 1xRTT):
+ * <table>
+ * <thead><tr><th>RSSI Range (dBm)</th><th>ASU Value</th></tr><thead>
+ * <tbody>
+ * <tr><td>-75..</td><td>16</td></tr>
+ * <tr><td>-82..-76</td><td>8</td></tr>
+ * <tr><td>-90..-83</td><td>4</td></tr>
+ * <tr><td>-95..-91</td><td>2</td></tr>
+ * <tr><td>-100..-96</td><td>1</td></tr>
+ * <tr><td>..-101</td><td>99</td></tr>
+ * </tbody>
+ * </table>
+ * <table>
+ * <thead><tr><th>Ec/Io Range (dB)</th><th>ASU Value</th></tr><thead>
+ * <tbody>
+ * <tr><td>-90..</td><td>16</td></tr>
+ * <tr><td>-100..-91</td><td>8</td></tr>
+ * <tr><td>-115..-101</td><td>4</td></tr>
+ * <tr><td>-130..-116</td><td>2</td></tr>
+ * <tr><td>--150..-131</td><td>1</td></tr>
+ * <tr><td>..-151</td><td>99</td></tr>
+ * </tbody>
+ * </table>
+ * @return 1xRTT Level in Android ASU {1,2,4,8,16,99}
*/
@Override
public int getAsuLevel() {
@@ -220,6 +258,63 @@
}
/**
+ * Get the EVDO Level in (Android) ASU.
+ *
+ * There is no standard definition of ASU for CDMA; however, Android defines it as the
+ * the lesser of the following two results (for EVDO):
+ * <table>
+ * <thead><tr><th>RSSI Range (dBm)</th><th>ASU Value</th></tr><thead>
+ * <tbody>
+ * <tr><td>-65..</td><td>16</td></tr>
+ * <tr><td>-75..-66</td><td>8</td></tr>
+ * <tr><td>-85..-76</td><td>4</td></tr>
+ * <tr><td>-95..-86</td><td>2</td></tr>
+ * <tr><td>-105..-96</td><td>1</td></tr>
+ * <tr><td>..-106</td><td>99</td></tr>
+ * </tbody>
+ * </table>
+ * <table>
+ * <thead><tr><th>SNR Range (unitless)</th><th>ASU Value</th></tr><thead>
+ * <tbody>
+ * <tr><td>7..</td><td>16</td></tr>
+ * <tr><td>6</td><td>8</td></tr>
+ * <tr><td>5</td><td>4</td></tr>
+ * <tr><td>3..4</td><td>2</td></tr>
+ * <tr><td>1..2</td><td>1</td></tr>
+ * <tr><td>0</td><td>99</td></tr>
+ * </tbody>
+ * </table>
+ *
+ * @return EVDO Level in Android ASU {1,2,4,8,16,99}
+ *
+ * @hide
+ */
+ public int getEvdoAsuLevel() {
+ int evdoDbm = getEvdoDbm();
+ int evdoSnr = getEvdoSnr();
+ int levelEvdoDbm;
+ int levelEvdoSnr;
+
+ if (evdoDbm >= -65) levelEvdoDbm = 16;
+ else if (evdoDbm >= -75) levelEvdoDbm = 8;
+ else if (evdoDbm >= -85) levelEvdoDbm = 4;
+ else if (evdoDbm >= -95) levelEvdoDbm = 2;
+ else if (evdoDbm >= -105) levelEvdoDbm = 1;
+ else levelEvdoDbm = 99;
+
+ if (evdoSnr >= 7) levelEvdoSnr = 16;
+ else if (evdoSnr >= 6) levelEvdoSnr = 8;
+ else if (evdoSnr >= 5) levelEvdoSnr = 4;
+ else if (evdoSnr >= 3) levelEvdoSnr = 2;
+ else if (evdoSnr >= 1) levelEvdoSnr = 1;
+ else levelEvdoSnr = 99;
+
+ int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ if (DBG) log("getEvdoAsuLevel=" + level);
+ return level;
+ }
+
+ /**
* Get the signal strength as dBm
*/
@Override
@@ -237,6 +332,7 @@
public int getCdmaDbm() {
return mCdmaDbm;
}
+
/** @hide */
public void setCdmaDbm(int cdmaDbm) {
mCdmaDbm = cdmaDbm;
@@ -248,6 +344,7 @@
public int getCdmaEcio() {
return mCdmaEcio;
}
+
/** @hide */
public void setCdmaEcio(int cdmaEcio) {
mCdmaEcio = cdmaEcio;
@@ -259,6 +356,7 @@
public int getEvdoDbm() {
return mEvdoDbm;
}
+
/** @hide */
public void setEvdoDbm(int evdoDbm) {
mEvdoDbm = evdoDbm;
@@ -270,6 +368,7 @@
public int getEvdoEcio() {
return mEvdoEcio;
}
+
/** @hide */
public void setEvdoEcio(int evdoEcio) {
mEvdoEcio = evdoEcio;
@@ -281,6 +380,7 @@
public int getEvdoSnr() {
return mEvdoSnr;
}
+
/** @hide */
public void setEvdoSnr(int evdoSnr) {
mEvdoSnr = evdoSnr;
@@ -288,28 +388,29 @@
@Override
public int hashCode() {
- return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr);
+ return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr, mLevel);
+ }
+
+ private static final CellSignalStrengthCdma sInvalid = new CellSignalStrengthCdma();
+
+ /** @hide */
+ @Override
+ public boolean isValid() {
+ return !this.equals(sInvalid);
}
@Override
public boolean equals (Object o) {
CellSignalStrengthCdma s;
-
- try {
- s = (CellSignalStrengthCdma) o;
- } catch (ClassCastException ex) {
- return false;
- }
-
- if (o == null) {
- return false;
- }
+ if (!(o instanceof CellSignalStrengthCdma)) return false;
+ s = (CellSignalStrengthCdma) o;
return mCdmaDbm == s.mCdmaDbm
&& mCdmaEcio == s.mCdmaEcio
&& mEvdoDbm == s.mEvdoDbm
&& mEvdoEcio == s.mEvdoEcio
- && mEvdoSnr == s.mEvdoSnr;
+ && mEvdoSnr == s.mEvdoSnr
+ && mLevel == s.mLevel;
}
/**
@@ -322,7 +423,8 @@
+ " cdmaEcio=" + mCdmaEcio
+ " evdoDbm=" + mEvdoDbm
+ " evdoEcio=" + mEvdoEcio
- + " evdoSnr=" + mEvdoSnr;
+ + " evdoSnr=" + mEvdoSnr
+ + " level=" + mLevel;
}
/** Implement the Parcelable interface */
@@ -334,6 +436,7 @@
dest.writeInt(mEvdoDbm);
dest.writeInt(mEvdoEcio);
dest.writeInt(mEvdoSnr);
+ dest.writeInt(mLevel);
}
/**
@@ -349,6 +452,7 @@
mEvdoDbm = in.readInt();
mEvdoEcio = in.readInt();
mEvdoSnr = in.readInt();
+ mLevel = in.readInt();
if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index e906f46..7b29f69 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -19,6 +19,7 @@
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.telephony.Rlog;
import java.util.Objects;
@@ -31,16 +32,18 @@
private static final String LOG_TAG = "CellSignalStrengthGsm";
private static final boolean DBG = false;
- private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
- private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
- private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
+ private static final int GSM_RSSI_MAX = -51;
+ private static final int GSM_RSSI_GREAT = -89;
+ private static final int GSM_RSSI_GOOD = -97;
+ private static final int GSM_RSSI_MODERATE = -103;
+ private static final int GSM_RSSI_POOR = -107;
+ private int mRssi; // in dBm [-113, -51] or UNAVAILABLE
@UnsupportedAppUsage
- private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
- @UnsupportedAppUsage
- private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- @UnsupportedAppUsage
+ private int mBitErrorRate; // bit error rate (0-7, 99) TS 27.007 8.5 or UNAVAILABLE
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown
+ private int mLevel;
/** @hide */
@UnsupportedAppUsage
@@ -49,15 +52,17 @@
}
/** @hide */
- public CellSignalStrengthGsm(int ss, int ber) {
- this(ss, ber, CellInfo.UNAVAILABLE);
+ public CellSignalStrengthGsm(int rssi, int ber, int ta) {
+ mRssi = inRangeOrUnavailable(rssi, -113, -51);
+ mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+ mTimingAdvance = inRangeOrUnavailable(ta, 0, 219);
+ updateLevel(null, null);
}
/** @hide */
- public CellSignalStrengthGsm(int ss, int ber, int ta) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- mTimingAdvance = ta;
+ public CellSignalStrengthGsm(android.hardware.radio.V1_0.GsmSignalStrength gsm) {
+ // Convert from HAL values as part of construction.
+ this(getRssiDbmFromAsu(gsm.signalStrength), gsm.bitErrorRate, gsm.timingAdvance);
}
/** @hide */
@@ -67,9 +72,10 @@
/** @hide */
protected void copyFrom(CellSignalStrengthGsm s) {
- mSignalStrength = s.mSignalStrength;
+ mRssi = s.mRssi;
mBitErrorRate = s.mBitErrorRate;
mTimingAdvance = s.mTimingAdvance;
+ mLevel = s.mLevel;
}
/** @hide */
@@ -81,9 +87,10 @@
/** @hide */
@Override
public void setDefaultValues() {
- mSignalStrength = CellInfo.UNAVAILABLE;
+ mRssi = CellInfo.UNAVAILABLE;
mBitErrorRate = CellInfo.UNAVAILABLE;
mTimingAdvance = CellInfo.UNAVAILABLE;
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
/**
@@ -94,20 +101,18 @@
*/
@Override
public int getLevel() {
- int level;
+ return mLevel;
+ }
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int asu = mSignalStrength;
- if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (asu >= GSM_SIGNAL_STRENGTH_GREAT) level = SIGNAL_STRENGTH_GREAT;
- else if (asu >= GSM_SIGNAL_STRENGTH_GOOD) level = SIGNAL_STRENGTH_GOOD;
- else if (asu >= GSM_SIGNAL_STRENGTH_MODERATE) level = SIGNAL_STRENGTH_MODERATE;
- else level = SIGNAL_STRENGTH_POOR;
- if (DBG) log("getLevel=" + level);
- return level;
+ /** @hide */
+ @Override
+ public void updateLevel(PersistableBundle cc, ServiceState ss) {
+ if (mRssi > GSM_RSSI_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (mRssi >= GSM_RSSI_GREAT) mLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mRssi >= GSM_RSSI_GOOD) mLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mRssi >= GSM_RSSI_MODERATE) mLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mRssi >= GSM_RSSI_POOR) mLevel = SIGNAL_STRENGTH_POOR;
+ else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
/**
@@ -126,55 +131,52 @@
*/
@Override
public int getDbm() {
- int dBm;
-
- int level = mSignalStrength;
- int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
- if (asu != CellInfo.UNAVAILABLE) {
- dBm = -113 + (2 * asu);
- } else {
- dBm = CellInfo.UNAVAILABLE;
- }
- if (DBG) log("getDbm=" + dBm);
- return dBm;
+ return mRssi;
}
/**
- * Get the signal level as an asu value between 0..31, 99 is unknown
+ * Get the RSSI in ASU.
+ *
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ *
+ * @return RSSI in ASU 0..31, 99, or UNAVAILABLE
*/
@Override
public int getAsuLevel() {
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int level = mSignalStrength;
- if (DBG) log("getAsuLevel=" + level);
- return level;
+ return getAsuFromRssiDbm(mRssi);
+ }
+
+ /**
+ * Return the Bit Error Rate
+ * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE.
+ * @hide
+ */
+ public int getBitErrorRate() {
+ return mBitErrorRate;
}
@Override
public int hashCode() {
- return Objects.hash(mSignalStrength, mBitErrorRate, mTimingAdvance);
+ return Objects.hash(mRssi, mBitErrorRate, mTimingAdvance);
+ }
+
+ private static final CellSignalStrengthGsm sInvalid = new CellSignalStrengthGsm();
+
+ /** @hide */
+ @Override
+ public boolean isValid() {
+ return !this.equals(sInvalid);
}
@Override
- public boolean equals (Object o) {
- CellSignalStrengthGsm s;
+ public boolean equals(Object o) {
+ if (!(o instanceof CellSignalStrengthGsm)) return false;
+ CellSignalStrengthGsm s = (CellSignalStrengthGsm) o;
- try {
- s = (CellSignalStrengthGsm) o;
- } catch (ClassCastException ex) {
- return false;
- }
-
- if (o == null) {
- return false;
- }
-
- return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate &&
- s.mTimingAdvance == mTimingAdvance;
+ return mRssi == s.mRssi
+ && mBitErrorRate == s.mBitErrorRate
+ && mTimingAdvance == s.mTimingAdvance
+ && mLevel == s.mLevel;
}
/**
@@ -183,18 +185,20 @@
@Override
public String toString() {
return "CellSignalStrengthGsm:"
- + " ss=" + mSignalStrength
+ + " rssi=" + mRssi
+ " ber=" + mBitErrorRate
- + " mTa=" + mTimingAdvance;
+ + " mTa=" + mTimingAdvance
+ + " mLevel=" + mLevel;
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
- dest.writeInt(mSignalStrength);
+ dest.writeInt(mRssi);
dest.writeInt(mBitErrorRate);
dest.writeInt(mTimingAdvance);
+ dest.writeInt(mLevel);
}
/**
@@ -202,9 +206,10 @@
* where the token is already been processed.
*/
private CellSignalStrengthGsm(Parcel in) {
- mSignalStrength = in.readInt();
+ mRssi = in.readInt();
mBitErrorRate = in.readInt();
mTimingAdvance = in.readInt();
+ mLevel = in.readInt();
if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index d075394..893dbe3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -19,7 +19,9 @@
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -28,7 +30,7 @@
public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
private static final String LOG_TAG = "CellSignalStrengthLte";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
/**
* Indicates the unknown or undetectable RSSI value in ASU.
@@ -49,18 +51,23 @@
*/
private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE = 0;
- @UnsupportedAppUsage
- private int mSignalStrength;
- @UnsupportedAppUsage
+ private static final int MAX_LTE_RSRP = -44;
+ private static final int MIN_LTE_RSRP = -140;
+
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+ private int mSignalStrength; // To be removed
+ private int mRssi;
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
private int mRsrp;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
private int mRsrq;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
private int mRssnr;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
private int mCqi;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
private int mTimingAdvance;
+ private int mLevel;
/** @hide */
@UnsupportedAppUsage
@@ -68,15 +75,38 @@
setDefaultValues();
}
+ /**
+ * Construct a cell signal strength
+ *
+ * @param rssi in dBm [-113,-51], UNKNOWN
+ * @param rsrp in dBm [-140,-43], UNKNOWN
+ * @param rsrq in dB [-20,-3], UNKNOWN
+ * @param rssnr in 10*dB [-200, +300], UNKNOWN
+ * @param cqi [0, 15], UNKNOWN
+ * @param timingAdvance [0, 1282], UNKNOWN
+ *
+ */
/** @hide */
- public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
+ public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi,
int timingAdvance) {
- mSignalStrength = signalStrength;
- mRsrp = rsrp;
- mRsrq = rsrq;
- mRssnr = rssnr;
- mCqi = cqi;
- mTimingAdvance = timingAdvance;
+
+ mRssi = inRangeOrUnavailable(rssi, -113, -51);
+ mSignalStrength = mRssi;
+ mRsrp = inRangeOrUnavailable(rsrp, -140, -43);
+ mRsrq = inRangeOrUnavailable(rsrq, -20, -3);
+ mRssnr = inRangeOrUnavailable(rssnr, -200, 300);
+ mCqi = inRangeOrUnavailable(cqi, 0, 15);
+ mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
+ updateLevel(null, null);
+ }
+
+ /** @hide */
+ public CellSignalStrengthLte(android.hardware.radio.V1_0.LteSignalStrength lte) {
+ // Convert from HAL values as part of construction.
+ this(convertRssiAsuToDBm(lte.signalStrength),
+ lte.rsrp != CellInfo.UNAVAILABLE ? -lte.rsrp : lte.rsrp,
+ lte.rsrq != CellInfo.UNAVAILABLE ? -lte.rsrq : lte.rsrq,
+ lte.rssnr, lte.cqi, lte.timingAdvance);
}
/** @hide */
@@ -87,11 +117,13 @@
/** @hide */
protected void copyFrom(CellSignalStrengthLte s) {
mSignalStrength = s.mSignalStrength;
+ mRssi = s.mRssi;
mRsrp = s.mRsrp;
mRsrq = s.mRsrq;
mRssnr = s.mRssnr;
mCqi = s.mCqi;
mTimingAdvance = s.mTimingAdvance;
+ mLevel = s.mLevel;
}
/** @hide */
@@ -104,11 +136,13 @@
@Override
public void setDefaultValues() {
mSignalStrength = CellInfo.UNAVAILABLE;
+ mRssi = CellInfo.UNAVAILABLE;
mRsrp = CellInfo.UNAVAILABLE;
mRsrq = CellInfo.UNAVAILABLE;
mRssnr = CellInfo.UNAVAILABLE;
mCqi = CellInfo.UNAVAILABLE;
mTimingAdvance = CellInfo.UNAVAILABLE;
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
/**
@@ -119,34 +153,106 @@
*/
@Override
public int getLevel() {
- int levelRsrp = 0;
- int levelRssnr = 0;
+ return mLevel;
+ }
- if (mRsrp == CellInfo.UNAVAILABLE) levelRsrp = 0;
- else if (mRsrp >= -95) levelRsrp = SIGNAL_STRENGTH_GREAT;
- else if (mRsrp >= -105) levelRsrp = SIGNAL_STRENGTH_GOOD;
- else if (mRsrp >= -115) levelRsrp = SIGNAL_STRENGTH_MODERATE;
- else levelRsrp = SIGNAL_STRENGTH_POOR;
+ // Lifted from Default carrier configs and max range of RSRP
+ private static final int[] sThresholds = new int[]{-115, -105, -95, -85};
+ private static final int sRsrpBoost = 0;
- // See RIL_LTE_SignalStrength in ril.h
- if (mRssnr == CellInfo.UNAVAILABLE) levelRssnr = 0;
- else if (mRssnr >= 45) levelRssnr = SIGNAL_STRENGTH_GREAT;
- else if (mRssnr >= 10) levelRssnr = SIGNAL_STRENGTH_GOOD;
- else if (mRssnr >= -30) levelRssnr = SIGNAL_STRENGTH_MODERATE;
- else levelRssnr = SIGNAL_STRENGTH_POOR;
-
- int level;
- if (mRsrp == CellInfo.UNAVAILABLE) {
- level = levelRssnr;
- } else if (mRssnr == CellInfo.UNAVAILABLE) {
- level = levelRsrp;
+ /** @hide */
+ @Override
+ public void updateLevel(PersistableBundle cc, ServiceState ss) {
+ int[] thresholds;
+ boolean rsrpOnly;
+ if (cc == null) {
+ thresholds = sThresholds;
+ rsrpOnly = false;
} else {
- level = (levelRssnr < levelRsrp) ? levelRssnr : levelRsrp;
+ rsrpOnly = cc.getBoolean(
+ CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
+ thresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+ if (thresholds == null) thresholds = sThresholds;
+ if (DBG) log("updateLevel() carrierconfig - rsrpOnly="
+ + rsrpOnly + ", thresholds=" + Arrays.toString(thresholds));
}
- if (DBG) log("Lte rsrp level: " + levelRsrp
- + " snr level: " + levelRssnr + " level: " + level);
- return level;
+
+ int rsrpBoost = 0;
+ if (ss != null) {
+ rsrpBoost = ss.getLteEarfcnRsrpBoost();
+ }
+
+ int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ int rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ int snrIconLevel = -1;
+
+ int rsrp = mRsrp + rsrpBoost;
+
+ if (rsrp < MIN_LTE_RSRP || rsrp > MAX_LTE_RSRP) {
+ rsrpIconLevel = -1;
+ } else {
+ rsrpIconLevel = thresholds.length;
+ while (rsrpIconLevel > 0 && rsrp < thresholds[rsrpIconLevel - 1]) rsrpIconLevel--;
+ }
+
+ if (rsrpOnly) {
+ if (DBG) log("updateLevel() - rsrp = " + rsrpIconLevel);
+ if (rsrpIconLevel != -1) {
+ mLevel = rsrpIconLevel;
+ return;
+ }
+ }
+
+ /*
+ * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
+ * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
+ * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
+ * Icon Only
+ */
+ if (mRssnr > 300) snrIconLevel = -1;
+ else if (mRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
+ else if (mRssnr >= -200)
+ snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ if (DBG) log("updateLevel() - rsrp:" + mRsrp + " snr:" + mRssnr + " rsrpIconLevel:"
+ + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
+ + " lteRsrpBoost:" + sRsrpBoost);
+
+ /* Choose a measurement type to use for notification */
+ if (snrIconLevel != -1 && rsrpIconLevel != -1) {
+ /*
+ * The number of bars displayed shall be the smaller of the bars
+ * associated with LTE RSRP and the bars associated with the LTE
+ * RS_SNR
+ */
+ mLevel = (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
+ return;
+ }
+
+ if (snrIconLevel != -1) {
+ mLevel = snrIconLevel;
+ return;
+ }
+
+ if (rsrpIconLevel != -1) {
+ mLevel = rsrpIconLevel;
+ return;
+ }
+
+ if (mRssi > -51) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (mRssi >= -89) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mRssi >= -97) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mRssi >= -103) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mRssi >= -113) rssiIconLevel = SIGNAL_STRENGTH_POOR;
+ else rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ if (DBG) log("getLteLevel - rssi:" + mRssi + " rssiIconLevel:"
+ + rssiIconLevel);
+ mLevel = rssiIconLevel;
}
/**
@@ -169,7 +275,7 @@
* @return the RSSI if available or {@link CellInfo#UNAVAILABLE} if unavailable.
*/
public int getRssi() {
- return convertRssiAsuToDBm(mSignalStrength);
+ return mRssi;
}
/**
@@ -212,13 +318,16 @@
}
/**
- * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+ * Get the RSRP in ASU.
+ *
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ *
+ * @return RSCP in ASU 0..97, 255, or UNAVAILABLE
*/
@Override
public int getAsuLevel() {
int lteAsuLevel = 99;
- int lteDbm = getDbm();
+ int lteDbm = mRsrp;
if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99;
else if (lteDbm <= -140) lteAsuLevel = 0;
else if (lteDbm >= -43) lteAsuLevel = 97;
@@ -241,29 +350,31 @@
@Override
public int hashCode() {
- return Objects.hash(mSignalStrength, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
+ return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance, mLevel);
+ }
+
+ private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte();
+
+ /** @hide */
+ @Override
+ public boolean isValid() {
+ return !this.equals(sInvalid);
}
@Override
public boolean equals (Object o) {
CellSignalStrengthLte s;
- try {
- s = (CellSignalStrengthLte) o;
- } catch (ClassCastException ex) {
- return false;
- }
+ if (!(o instanceof CellSignalStrengthLte)) return false;
+ s = (CellSignalStrengthLte) o;
- if (o == null) {
- return false;
- }
-
- return mSignalStrength == s.mSignalStrength
+ return mRssi == s.mRssi
&& mRsrp == s.mRsrp
&& mRsrq == s.mRsrq
&& mRssnr == s.mRssnr
&& mCqi == s.mCqi
- && mTimingAdvance == s.mTimingAdvance;
+ && mTimingAdvance == s.mTimingAdvance
+ && mLevel == s.mLevel;
}
/**
@@ -272,27 +383,29 @@
@Override
public String toString() {
return "CellSignalStrengthLte:"
- + " ss=" + mSignalStrength
+ + " rssi=" + mRssi
+ " rsrp=" + mRsrp
+ " rsrq=" + mRsrq
+ " rssnr=" + mRssnr
+ " cqi=" + mCqi
- + " ta=" + mTimingAdvance;
+ + " ta=" + mTimingAdvance
+ + " level=" + mLevel;
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
- dest.writeInt(mSignalStrength);
+ dest.writeInt(mRssi);
// Need to multiply rsrp and rsrq by -1
// to ensure consistency when reading values written here
// unless the values are invalid
- dest.writeInt(mRsrp * (mRsrp != CellInfo.UNAVAILABLE ? -1 : 1));
- dest.writeInt(mRsrq * (mRsrq != CellInfo.UNAVAILABLE ? -1 : 1));
+ dest.writeInt(mRsrp);
+ dest.writeInt(mRsrq);
dest.writeInt(mRssnr);
dest.writeInt(mCqi);
dest.writeInt(mTimingAdvance);
+ dest.writeInt(mLevel);
}
/**
@@ -300,16 +413,14 @@
* where the token is already been processed.
*/
private CellSignalStrengthLte(Parcel in) {
- mSignalStrength = in.readInt();
- // rsrp and rsrq are written into the parcel as positive values.
- // Need to convert into negative values unless the values are invalid
+ mRssi = in.readInt();
+ mSignalStrength = mRssi;
mRsrp = in.readInt();
- if (mRsrp != CellInfo.UNAVAILABLE) mRsrp *= -1;
mRsrq = in.readInt();
- if (mRsrq != CellInfo.UNAVAILABLE) mRsrq *= -1;
mRssnr = in.readInt();
mCqi = in.readInt();
mTimingAdvance = in.readInt();
+ mLevel = in.readInt();
if (DBG) log("CellSignalStrengthLte(Parcel): " + toString());
}
@@ -342,13 +453,12 @@
}
private static int convertRssiAsuToDBm(int rssiAsu) {
- if (rssiAsu != SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN
- && (rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
- || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) {
- Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu);
+ if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
return CellInfo.UNAVAILABLE;
}
- if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+ if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
+ || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) {
+ Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu);
return CellInfo.UNAVAILABLE;
}
return -113 + (2 * rssiAsu);
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 8079242..061cd4b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import java.util.Objects;
@@ -48,6 +49,12 @@
private int mSsRsrp;
private int mSsRsrq;
private int mSsSinr;
+ private int mLevel;
+
+ /** @hide */
+ public CellSignalStrengthNr() {
+ setDefaultValues();
+ }
/**
* @param csiRsrp CSI reference signal received power.
@@ -60,12 +67,13 @@
*/
public CellSignalStrengthNr(
int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) {
- mCsiRsrp = csiRsrp;
- mCsiRsrq = csiRsrq;
- mCsiSinr = csiSinr;
- mSsRsrp = ssRsrp;
- mSsRsrq = ssRsrq;
- mSsSinr = ssSinr;
+ mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44);
+ mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3);
+ mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23);
+ mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44);
+ mSsRsrq = inRangeOrUnavailable(ssRsrq, -20, -3);
+ mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40);
+ updateLevel(null, null);
}
/**
@@ -142,6 +150,7 @@
dest.writeInt(mSsRsrp);
dest.writeInt(mSsRsrq);
dest.writeInt(mSsSinr);
+ dest.writeInt(mLevel);
}
private CellSignalStrengthNr(Parcel in) {
@@ -151,6 +160,7 @@
mSsRsrp = in.readInt();
mSsRsrq = in.readInt();
mSsSinr = in.readInt();
+ mLevel = in.readInt();
}
/** @hide */
@@ -162,27 +172,36 @@
mSsRsrp = CellInfo.UNAVAILABLE;
mSsRsrq = CellInfo.UNAVAILABLE;
mSsSinr = CellInfo.UNAVAILABLE;
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
@Override
public int getLevel() {
+ return mLevel;
+ }
+
+ /** @hide */
+ @Override
+ public void updateLevel(PersistableBundle cc, ServiceState ss) {
if (mCsiRsrp == CellInfo.UNAVAILABLE) {
- return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
} else if (mCsiRsrp >= SIGNAL_GREAT_THRESHOLD) {
- return SIGNAL_STRENGTH_GREAT;
+ mLevel = SIGNAL_STRENGTH_GREAT;
} else if (mCsiRsrp >= SIGNAL_GOOD_THRESHOLD) {
- return SIGNAL_STRENGTH_GOOD;
+ mLevel = SIGNAL_STRENGTH_GOOD;
} else if (mCsiRsrp >= SIGNAL_MODERATE_THRESHOLD) {
- return SIGNAL_STRENGTH_MODERATE;
+ mLevel = SIGNAL_STRENGTH_MODERATE;
} else {
- return SIGNAL_STRENGTH_POOR;
+ mLevel = SIGNAL_STRENGTH_POOR;
}
}
/**
- * Calculates the NR signal as an asu value between 0..97, 99 is unknown.
- * Asu is calculated based on 3GPP RSRP, refer to 3GPP TS 27.007 section 8.69.
- * @return an integer represent the asu level of the signal strength.
+ * Get the RSRP in ASU.
+ *
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ *
+ * @return RSCP in ASU 0..97, 255, or UNAVAILABLE
*/
@Override
public int getAsuLevel() {
@@ -206,15 +225,33 @@
}
/** @hide */
+ public CellSignalStrengthNr(CellSignalStrengthNr s) {
+ mCsiRsrp = s.mCsiRsrp;
+ mCsiRsrq = s.mCsiRsrq;
+ mCsiSinr = s.mCsiSinr;
+ mSsRsrp = s.mSsRsrp;
+ mSsRsrq = s.mSsRsrq;
+ mSsSinr = s.mSsSinr;
+ mLevel = s.mLevel;
+ }
+
+ /** @hide */
@Override
- public CellSignalStrength copy() {
- return new CellSignalStrengthNr(
- mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr);
+ public CellSignalStrengthNr copy() {
+ return new CellSignalStrengthNr(this);
}
@Override
public int hashCode() {
- return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr);
+ return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr, mLevel);
+ }
+
+ private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr();
+
+ /** @hide */
+ @Override
+ public boolean isValid() {
+ return !this.equals(sInvalid);
}
@Override
@@ -222,7 +259,8 @@
if (obj instanceof CellSignalStrengthNr) {
CellSignalStrengthNr o = (CellSignalStrengthNr) obj;
return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr
- && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr;
+ && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr
+ && mLevel == o.mLevel;
}
return false;
}
@@ -237,6 +275,7 @@
.append(" ssRsrp = " + mSsRsrp)
.append(" ssRsrq = " + mSsRsrq)
.append(" ssSinr = " + mSsSinr)
+ .append(" level = " + mLevel)
.append(" }")
.toString();
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index 4d040cc..6f52b85 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import java.util.Objects;
@@ -31,27 +32,53 @@
private static final String LOG_TAG = "CellSignalStrengthTdscdma";
private static final boolean DBG = false;
- private static final int TDSCDMA_SIGNAL_STRENGTH_GREAT = 12;
- private static final int TDSCDMA_SIGNAL_STRENGTH_GOOD = 8;
- private static final int TDSCDMA_SIGNAL_STRENGTH_MODERATE = 5;
+ private static final int TDSCDMA_RSSI_MAX = -51;
+ private static final int TDSCDMA_RSSI_GREAT = -77;
+ private static final int TDSCDMA_RSSI_GOOD = -87;
+ private static final int TDSCDMA_RSSI_MODERATE = -97;
+ private static final int TDSCDMA_RSSI_POOR = -107;
- private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
- // or CellInfo.UNAVAILABLE if unknown
+ private static final int TDSCDMA_RSCP_MIN = -120;
+ private static final int TDSCDMA_RSCP_MAX = -24;
+
+ private int mRssi; // in dBm [-113, -51], CellInfo.UNAVAILABLE
+
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
// CellInfo.UNAVAILABLE if unknown
- private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or
+ private int mRscp; // Pilot Power in dBm [-120, -24] or CellInfo.UNAVAILABLE
// CellInfo.UNAVAILABLE if unknown
+ private int mLevel;
+
/** @hide */
public CellSignalStrengthTdscdma() {
setDefaultValues();
}
+ /**
+ * @param rssi in dBm [-113, -51] or UNAVAILABLE
+ * @param ber [0-7], 99 or UNAVAILABLE
+ * @param rscp in dBm [-120, -24] or UNAVAILABLE
+ * @hide */
+ public CellSignalStrengthTdscdma(int rssi, int ber, int rscp) {
+ mRssi = inRangeOrUnavailable(rssi, -113, -51);
+ mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+ mRscp = inRangeOrUnavailable(rscp, -120, -24);
+ updateLevel(null, null);
+ }
+
/** @hide */
- public CellSignalStrengthTdscdma(int ss, int ber, int rscp) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- mRscp = rscp;
+ public CellSignalStrengthTdscdma(android.hardware.radio.V1_0.TdScdmaSignalStrength tdscdma) {
+ // Convert from HAL values as part of construction.
+ this(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+ tdscdma.rscp != CellInfo.UNAVAILABLE ? -tdscdma.rscp : tdscdma.rscp);
+ }
+
+ /** @hide */
+ public CellSignalStrengthTdscdma(android.hardware.radio.V1_2.TdscdmaSignalStrength tdscdma) {
+ // Convert from HAL values as part of construction.
+ this(getRssiDbmFromAsu(tdscdma.signalStrength),
+ tdscdma.bitErrorRate, getRscpDbmFromAsu(tdscdma.rscp));
}
/** @hide */
@@ -61,9 +88,10 @@
/** @hide */
protected void copyFrom(CellSignalStrengthTdscdma s) {
- mSignalStrength = s.mSignalStrength;
+ mRssi = s.mRssi;
mBitErrorRate = s.mBitErrorRate;
mRscp = s.mRscp;
+ mLevel = s.mLevel;
}
/** @hide */
@@ -75,9 +103,10 @@
/** @hide */
@Override
public void setDefaultValues() {
- mSignalStrength = CellInfo.UNAVAILABLE;
+ mRssi = CellInfo.UNAVAILABLE;
mBitErrorRate = CellInfo.UNAVAILABLE;
mRscp = CellInfo.UNAVAILABLE;
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
/**
@@ -88,26 +117,18 @@
*/
@Override
public int getLevel() {
- int level;
+ return mLevel;
+ }
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int asu = mSignalStrength;
- if (asu <= 2 || asu == 99) {
- level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_GREAT) {
- level = SIGNAL_STRENGTH_GREAT;
- } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_GOOD) {
- level = SIGNAL_STRENGTH_GOOD;
- } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_MODERATE) {
- level = SIGNAL_STRENGTH_MODERATE;
- } else {
- level = SIGNAL_STRENGTH_POOR;
- }
- if (DBG) log("getLevel=" + level);
- return level;
+ /** @hide */
+ @Override
+ public void updateLevel(PersistableBundle cc, ServiceState ss) {
+ if (mRssi > TDSCDMA_RSSI_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (mRssi >= TDSCDMA_RSSI_GREAT) mLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mRssi >= TDSCDMA_RSSI_GOOD) mLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mRssi >= TDSCDMA_RSSI_MODERATE) mLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mRssi >= TDSCDMA_RSSI_POOR) mLevel = SIGNAL_STRENGTH_POOR;
+ else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
/**
@@ -115,56 +136,55 @@
*/
@Override
public int getDbm() {
- int dBm;
-
- int level = mSignalStrength;
- int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
- if (asu != CellInfo.UNAVAILABLE) {
- dBm = -113 + (2 * asu);
- } else {
- dBm = CellInfo.UNAVAILABLE;
- }
- if (DBG) log("getDbm=" + dBm);
- return dBm;
+ return mRscp;
}
/**
- * Get the signal level as an asu value between 0..31, 99 is unknown
+ * Get the RSCP as dBm
+ * @hide
+ */
+ public int getRscp() {
+ return mRscp;
+ }
+
+ /**
+ * Get the RSCP in ASU.
+ *
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ *
+ * @return RSCP in ASU 0..96, 255, or UNAVAILABLE
*/
@Override
public int getAsuLevel() {
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int level = mSignalStrength;
- if (DBG) log("getAsuLevel=" + level);
- return level;
+ if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp);
+ // For historical reasons, if RSCP is unavailable, this API will very incorrectly return
+ // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+
+ if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi);
+ return getAsuFromRscpDbm(CellInfo.UNAVAILABLE);
}
@Override
public int hashCode() {
- return Objects.hash(mSignalStrength, mBitErrorRate);
+ return Objects.hash(mRssi, mBitErrorRate, mRscp, mLevel);
+ }
+
+ private static final CellSignalStrengthTdscdma sInvalid = new CellSignalStrengthTdscdma();
+
+ /** @hide */
+ @Override
+ public boolean isValid() {
+ return !this.equals(sInvalid);
}
@Override
public boolean equals(Object o) {
- CellSignalStrengthTdscdma s;
+ if (!(o instanceof CellSignalStrengthTdscdma)) return false;
+ CellSignalStrengthTdscdma s = (CellSignalStrengthTdscdma) o;
- try {
- s = (CellSignalStrengthTdscdma) o;
- } catch (ClassCastException ex) {
- return false;
- }
-
- if (o == null) {
- return false;
- }
-
- return mSignalStrength == s.mSignalStrength
+ return mRssi == s.mRssi
&& mBitErrorRate == s.mBitErrorRate
- && mRscp == s.mRscp;
+ && mRscp == s.mRscp
+ && mLevel == s.mLevel;
}
/**
@@ -173,18 +193,20 @@
@Override
public String toString() {
return "CellSignalStrengthTdscdma:"
- + " ss=" + mSignalStrength
+ + " rssi=" + mRssi
+ " ber=" + mBitErrorRate
- + " rscp=" + mRscp;
+ + " rscp=" + mRscp
+ + " level=" + mLevel;
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
- dest.writeInt(mSignalStrength);
+ dest.writeInt(mRssi);
dest.writeInt(mBitErrorRate);
dest.writeInt(mRscp);
+ dest.writeInt(mLevel);
}
/**
@@ -192,9 +214,10 @@
* where the token is already been processed.
*/
private CellSignalStrengthTdscdma(Parcel in) {
- mSignalStrength = in.readInt();
+ mRssi = in.readInt();
mBitErrorRate = in.readInt();
mRscp = in.readInt();
+ mLevel = in.readInt();
if (DBG) log("CellSignalStrengthTdscdma(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 0048cbd..88f6fbc 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -16,11 +16,14 @@
package android.telephony;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.StringDef;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.telephony.Rlog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -31,20 +34,32 @@
private static final String LOG_TAG = "CellSignalStrengthWcdma";
private static final boolean DBG = false;
- private static final int WCDMA_SIGNAL_STRENGTH_GREAT = 12;
- private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
- private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
+ private static final int WCDMA_RSSI_MAX = -51;
+ private static final int WCDMA_RSSI_GREAT = -77;
+ private static final int WCDMA_RSSI_GOOD = -87;
+ private static final int WCDMA_RSSI_MODERATE = -97;
+ private static final int WCDMA_RSSI_POOR = -107;
+ private static final int WCDMA_RSSI_MIN = -113;
- @UnsupportedAppUsage
- private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
- // or CellInfo.UNAVAILABLE if unknown
- @UnsupportedAppUsage
+ private static final int WCDMA_RSCP_MIN = -120;
+ private static final int WCDMA_RSCP_MAX = -24;
+
+ // TODO: Because these are used as values in CarrierConfig, they should be exposed somehow.
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({LEVEL_CALCULATION_METHOD_RSSI, LEVEL_CALCULATION_METHOD_RSCP})
+ public @interface LevelCalculationMethod {}
+ /** @hide */
+ public static final String LEVEL_CALCULATION_METHOD_RSSI = "rssi";
+ /** @hide */
+ public static final String LEVEL_CALCULATION_METHOD_RSCP = "rscp";
+
+ private int mRssi; // in dBm [-113, 51] or CellInfo.UNAVAILABLE if unknown
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
// CellInfo.UNAVAILABLE if unknown
- private int mRscp; // bit error rate (0-96, 255) as defined in TS 27.007 8.69 or
- // CellInfo.UNAVAILABLE if unknown
- private int mEcNo; // signal to noise radio (0-49, 255) as defined in TS 27.007 8.69 or
- // CellInfo.UNAVAILABLE if unknown
+ private int mRscp; // in dBm [-120, -24]
+ private int mEcNo; // range -24, 1, CellInfo.UNAVAILABLE if unknown
+ private int mLevel;
/** @hide */
public CellSignalStrengthWcdma() {
@@ -52,11 +67,28 @@
}
/** @hide */
- public CellSignalStrengthWcdma(int ss, int ber, int rscp, int ecno) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- mRscp = rscp;
- mEcNo = ecno;
+ public CellSignalStrengthWcdma(int rssi, int ber, int rscp, int ecno) {
+ mRssi = inRangeOrUnavailable(rssi, WCDMA_RSSI_MIN, WCDMA_RSSI_MAX);
+ mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+ mRscp = inRangeOrUnavailable(rscp, -120, -24);
+ mEcNo = inRangeOrUnavailable(ecno, -24, 1);
+ updateLevel(null, null);
+ }
+
+ /** @hide */
+ public CellSignalStrengthWcdma(android.hardware.radio.V1_0.WcdmaSignalStrength wcdma) {
+ // Convert from HAL values as part of construction.
+ this(getRssiDbmFromAsu(wcdma.signalStrength),
+ wcdma.bitErrorRate, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE);
+ }
+
+ /** @hide */
+ public CellSignalStrengthWcdma(android.hardware.radio.V1_2.WcdmaSignalStrength wcdma) {
+ // Convert from HAL values as part of construction.
+ this(getRssiDbmFromAsu(wcdma.base.signalStrength),
+ wcdma.base.bitErrorRate,
+ getRscpDbmFromAsu(wcdma.rscp),
+ getEcNoDbFromAsu(wcdma.ecno));
}
/** @hide */
@@ -66,10 +98,11 @@
/** @hide */
protected void copyFrom(CellSignalStrengthWcdma s) {
- mSignalStrength = s.mSignalStrength;
+ mRssi = s.mRssi;
mBitErrorRate = s.mBitErrorRate;
mRscp = s.mRscp;
mEcNo = s.mEcNo;
+ mLevel = s.mLevel;
}
/** @hide */
@@ -81,12 +114,17 @@
/** @hide */
@Override
public void setDefaultValues() {
- mSignalStrength = CellInfo.UNAVAILABLE;
+ mRssi = CellInfo.UNAVAILABLE;
mBitErrorRate = CellInfo.UNAVAILABLE;
mRscp = CellInfo.UNAVAILABLE;
mEcNo = CellInfo.UNAVAILABLE;
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
+ private static final String sLevelCalculationMethod = LEVEL_CALCULATION_METHOD_RSSI;
+ private static final int[] sThresholds = new int[]{
+ WCDMA_RSSI_POOR, WCDMA_RSSI_GOOD, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT};
+
/**
* Retrieve an abstract level value for the overall signal strength.
*
@@ -95,20 +133,49 @@
*/
@Override
public int getLevel() {
- int level;
+ return mLevel;
+ }
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int asu = mSignalStrength;
- if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (asu >= WCDMA_SIGNAL_STRENGTH_GREAT) level = SIGNAL_STRENGTH_GREAT;
- else if (asu >= WCDMA_SIGNAL_STRENGTH_GOOD) level = SIGNAL_STRENGTH_GOOD;
- else if (asu >= WCDMA_SIGNAL_STRENGTH_MODERATE) level = SIGNAL_STRENGTH_MODERATE;
- else level = SIGNAL_STRENGTH_POOR;
- if (DBG) log("getLevel=" + level);
- return level;
+ /** @hide */
+ @Override
+ public void updateLevel(PersistableBundle cc, ServiceState ss) {
+ String calcMethod;
+ int[] thresholds;
+
+ if (cc == null) {
+ calcMethod = sLevelCalculationMethod;
+ thresholds = sThresholds;
+ } else {
+ // TODO: abstract this entire thing into a series of functions
+ calcMethod = cc.getString(
+ CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING,
+ sLevelCalculationMethod);
+ thresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
+ if (thresholds == null) thresholds = sThresholds;
+ }
+
+ int level = thresholds.length;
+ switch (calcMethod) {
+ case LEVEL_CALCULATION_METHOD_RSCP:
+ if (mRscp < WCDMA_RSCP_MIN || mRscp > WCDMA_RSCP_MAX) {
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ return;
+ }
+ while (level > 0 && mRscp < thresholds[level - 1]) level--;
+ mLevel = level;
+ return;
+ case LEVEL_CALCULATION_METHOD_RSSI:
+ if (mRssi < WCDMA_RSSI_MIN || mRssi > WCDMA_RSSI_MAX) {
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ return;
+ }
+ while (level > 0 && mRssi < thresholds[level - 1]) level--;
+ mLevel = level;
+ return;
+ default:
+ mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ }
}
/**
@@ -116,57 +183,66 @@
*/
@Override
public int getDbm() {
- int dBm;
-
- int level = mSignalStrength;
- int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
- if (asu != CellInfo.UNAVAILABLE) {
- dBm = -113 + (2 * asu);
- } else {
- dBm = CellInfo.UNAVAILABLE;
- }
- if (DBG) log("getDbm=" + dBm);
- return dBm;
+ if (mRscp != CellInfo.UNAVAILABLE) return mRscp;
+ return mRssi;
}
/**
- * Get the signal level as an asu value between 0..31, 99 is unknown
- * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ * Get the RSCP in ASU.
+ *
+ * Asu is calculated based on 3GPP RSCP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ *
+ * @return RSCP in ASU 0..96, 255, or UNAVAILABLE
*/
@Override
public int getAsuLevel() {
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int level = mSignalStrength;
- if (DBG) log("getAsuLevel=" + level);
- return level;
+ if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp);
+ // For historical reasons, if RSCP is unavailable, this API will very incorrectly return
+ // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+
+ if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi);
+ return getAsuFromRscpDbm(CellInfo.UNAVAILABLE);
+ }
+
+ /**
+ * Get the signal strength as dBm
+ *
+ * @hide
+ */
+ public int getRssi() {
+ return mRssi;
+ }
+
+ /**
+ * Get the RSCP as dBm
+ * @hide
+ */
+ public int getRscp() {
+ return mRscp;
}
@Override
public int hashCode() {
- return Objects.hash(mSignalStrength, mBitErrorRate);
+ return Objects.hash(mRssi, mBitErrorRate, mRscp, mEcNo, mLevel);
+ }
+
+ private static final CellSignalStrengthWcdma sInvalid = new CellSignalStrengthWcdma();
+
+ /** @hide */
+ @Override
+ public boolean isValid() {
+ return !this.equals(sInvalid);
}
@Override
- public boolean equals (Object o) {
- CellSignalStrengthWcdma s;
+ public boolean equals(Object o) {
+ if (!(o instanceof CellSignalStrengthWcdma)) return false;
+ CellSignalStrengthWcdma s = (CellSignalStrengthWcdma) o;
- try {
- s = (CellSignalStrengthWcdma) o;
- } catch (ClassCastException ex) {
- return false;
- }
-
- if (o == null) {
- return false;
- }
-
- return mSignalStrength == s.mSignalStrength
+ return mRssi == s.mRssi
&& mBitErrorRate == s.mBitErrorRate
&& mRscp == s.mRscp
- && mEcNo == s.mEcNo;
+ && mEcNo == s.mEcNo
+ && mLevel == s.mLevel;
}
/**
@@ -175,20 +251,22 @@
@Override
public String toString() {
return "CellSignalStrengthWcdma:"
- + " ss=" + mSignalStrength
+ + " ss=" + mRssi
+ " ber=" + mBitErrorRate
+ " rscp=" + mRscp
- + " ecno=" + mEcNo;
+ + " ecno=" + mEcNo
+ + " level=" + mLevel;
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
- dest.writeInt(mSignalStrength);
+ dest.writeInt(mRssi);
dest.writeInt(mBitErrorRate);
dest.writeInt(mRscp);
dest.writeInt(mEcNo);
+ dest.writeInt(mLevel);
}
/**
@@ -196,10 +274,11 @@
* where the token is already been processed.
*/
private CellSignalStrengthWcdma(Parcel in) {
- mSignalStrength = in.readInt();
+ mRssi = in.readInt();
mBitErrorRate = in.readInt();
mRscp = in.readInt();
mEcNo = in.readInt();
+ mLevel = in.readInt();
if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c6f7d0e..c53b37d 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -15,151 +15,383 @@
*/
package android.telephony;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.Context;
import android.os.PersistableBundle;
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
- * Returned as the reason for a connection failure as defined
- * by RIL_DataCallFailCause in ril.h and some local errors.
+ * Returned as the reason for a data connection failure as defined by modem and some local errors.
* @hide
*/
-public enum DataFailCause {
- NONE(0),
+public final class DataFailCause {
+ /** There is no failure */
+ public static final int NONE = 0;
// This series of errors as specified by the standards
// specified in ril.h
- OPERATOR_BARRED(0x08), /* no retry */
- NAS_SIGNALLING(0x0E),
- LLC_SNDCP(0x19),
- INSUFFICIENT_RESOURCES(0x1A),
- MISSING_UNKNOWN_APN(0x1B), /* no retry */
- UNKNOWN_PDP_ADDRESS_TYPE(0x1C), /* no retry */
- USER_AUTHENTICATION(0x1D), /* no retry */
- ACTIVATION_REJECT_GGSN(0x1E), /* no retry */
- ACTIVATION_REJECT_UNSPECIFIED(0x1F),
- SERVICE_OPTION_NOT_SUPPORTED(0x20), /* no retry */
- SERVICE_OPTION_NOT_SUBSCRIBED(0x21), /* no retry */
- SERVICE_OPTION_OUT_OF_ORDER(0x22),
- NSAPI_IN_USE(0x23), /* no retry */
- REGULAR_DEACTIVATION(0x24), /* possibly restart radio, based on config */
- QOS_NOT_ACCEPTED(0x25),
- NETWORK_FAILURE(0x26),
- UMTS_REACTIVATION_REQ(0x27),
- FEATURE_NOT_SUPP(0x28),
- TFT_SEMANTIC_ERROR(0x29),
- TFT_SYTAX_ERROR(0x2A),
- UNKNOWN_PDP_CONTEXT(0x2B),
- FILTER_SEMANTIC_ERROR(0x2C),
- FILTER_SYTAX_ERROR(0x2D),
- PDP_WITHOUT_ACTIVE_TFT(0x2E),
- ONLY_IPV4_ALLOWED(0x32), /* no retry */
- ONLY_IPV6_ALLOWED(0x33), /* no retry */
- ONLY_SINGLE_BEARER_ALLOWED(0x34),
- ESM_INFO_NOT_RECEIVED(0x35),
- PDN_CONN_DOES_NOT_EXIST(0x36),
- MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED(0x37),
- MAX_ACTIVE_PDP_CONTEXT_REACHED(0x41),
- UNSUPPORTED_APN_IN_CURRENT_PLMN(0x42),
- INVALID_TRANSACTION_ID(0x51),
- MESSAGE_INCORRECT_SEMANTIC(0x5F),
- INVALID_MANDATORY_INFO(0x60),
- MESSAGE_TYPE_UNSUPPORTED(0x61),
- MSG_TYPE_NONCOMPATIBLE_STATE(0x62),
- UNKNOWN_INFO_ELEMENT(0x63),
- CONDITIONAL_IE_ERROR(0x64),
- MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE(0x65),
- PROTOCOL_ERRORS(0x6F), /* no retry */
- APN_TYPE_CONFLICT(0x70),
- INVALID_PCSCF_ADDR(0x71),
- INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN(0x72),
- EMM_ACCESS_BARRED(0x73),
- EMERGENCY_IFACE_ONLY(0x74),
- IFACE_MISMATCH(0x75),
- COMPANION_IFACE_IN_USE(0x76),
- IP_ADDRESS_MISMATCH(0x77),
- IFACE_AND_POL_FAMILY_MISMATCH(0x78),
- EMM_ACCESS_BARRED_INFINITE_RETRY(0x79),
- AUTH_FAILURE_ON_EMERGENCY_CALL(0x7A),
+ /** Operator determined barring. */
+ public static final int OPERATOR_BARRED = 0x08;
+ /** NAS signalling. */
+ public static final int NAS_SIGNALLING = 0x0E;
+ /** Logical Link Control (LLC) Sub Network Dependent Convergence Protocol (SNDCP). */
+ public static final int LLC_SNDCP = 0x19;
+ /** Insufficient resources. */
+ public static final int INSUFFICIENT_RESOURCES = 0x1A;
+ /** Missing or unknown APN. */
+ public static final int MISSING_UNKNOWN_APN = 0x1B; /* no retry */
+ /** Unknown Packet Data Protocol (PDP) address type. */
+ public static final int UNKNOWN_PDP_ADDRESS_TYPE = 0x1C; /* no retry */
+ /** User authentication. */
+ public static final int USER_AUTHENTICATION = 0x1D; /* no retry */
+ /** Activation rejected by Gateway GPRS Support Node (GGSN), Serving Gateway or PDN Gateway. */
+ public static final int ACTIVATION_REJECT_GGSN = 0x1E; /* no retry */
+ /** Activation rejected, unspecified. */
+ public static final int ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
+ /** Service option not supported. */
+ public static final int SERVICE_OPTION_NOT_SUPPORTED = 0x20; /* no retry */
+ /** Requested service option not subscribed. */
+ public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 0x21; /* no retry */
+ /** Service option temporarily out of order. */
+ public static final int SERVICE_OPTION_OUT_OF_ORDER = 0x22;
+ /** The Network Service Access Point Identifier (NSAPI) is in use. */
+ public static final int NSAPI_IN_USE = 0x23; /* no retry */
+ /* possibly restart radio, based on config */
+ /** Regular deactivation. */
+ public static final int REGULAR_DEACTIVATION = 0x24;
+ /** Quality of service (QoS) is not accepted. */
+ public static final int QOS_NOT_ACCEPTED = 0x25;
+ /** Network Failure. */
+ public static final int NETWORK_FAILURE = 0x26;
+ /** Universal Mobile Telecommunications System (UMTS) reactivation request. */
+ public static final int UMTS_REACTIVATION_REQ = 0x27;
+ /** Feature not supported. */
+ public static final int FEATURE_NOT_SUPP = 0x28;
+ /** Semantic error in the Traffic flow templates (TFT) operation. */
+ public static final int TFT_SEMANTIC_ERROR = 0x29;
+ /** Syntactical error in the Traffic flow templates (TFT) operation. */
+ public static final int TFT_SYTAX_ERROR = 0x2A;
+ /** Unknown Packet Data Protocol (PDP) context. */
+ public static final int UNKNOWN_PDP_CONTEXT = 0x2B;
+ /** Semantic errors in packet filter. */
+ public static final int FILTER_SEMANTIC_ERROR = 0x2C;
+ /** Syntactical errors in packet filter(s). */
+ public static final int FILTER_SYTAX_ERROR = 0x2D;
+ /** Packet Data Protocol (PDP) without active traffic flow template (TFT). */
+ public static final int PDP_WITHOUT_ACTIVE_TFT = 0x2E;
+ /** Packet Data Protocol (PDP) type IPv4 only allowed. */
+ public static final int ONLY_IPV4_ALLOWED = 0x32; /* no retry */
+ /** Packet Data Protocol (PDP) type IPv6 only allowed. */
+ public static final int ONLY_IPV6_ALLOWED = 0x33; /* no retry */
+ /** Single address bearers only allowed. */
+ public static final int ONLY_SINGLE_BEARER_ALLOWED = 0x34;
+ /** EPS Session Management (ESM) information is not received. */
+ public static final int ESM_INFO_NOT_RECEIVED = 0x35;
+ /** PDN connection does not exist. */
+ public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
+ /** Multiple connections to a same PDN is not allowed. */
+ public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
+ /** Packet Data Protocol (PDP) */
+ public static final int MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41;
+ /** Unsupported APN in current public land mobile network (PLMN). */
+ public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42;
+ /** Invalid transaction id. */
+ public static final int INVALID_TRANSACTION_ID = 0x51;
+ /** Incorrect message semantic. */
+ public static final int MESSAGE_INCORRECT_SEMANTIC = 0x5F;
+ /** Invalid mandatory information. */
+ public static final int INVALID_MANDATORY_INFO = 0x60;
+ /** Unsupported message type. */
+ public static final int MESSAGE_TYPE_UNSUPPORTED = 0x61;
+ /** Message type uncompatible. */
+ public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 0x62;
+ /** Unknown info element. */
+ public static final int UNKNOWN_INFO_ELEMENT = 0x63;
+ /** Conditional Information Element (IE) error. */
+ public static final int CONDITIONAL_IE_ERROR = 0x64;
+ /** Message and protocol state uncompatible. */
+ public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65;
+ /** Protocol errors. */
+ public static final int PROTOCOL_ERRORS = 0x6F; /* no retry */
+ /** APN type conflict. */
+ public static final int APN_TYPE_CONFLICT = 0x70;
+ /** Invalid Proxy-Call Session Control Function (P-CSCF) address. */
+ public static final int INVALID_PCSCF_ADDR = 0x71;
+ /** Internal data call preempt by high priority APN. */
+ public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72;
+ /** EPS (Evolved Packet System) Mobility Management (EMM) access barred. */
+ public static final int EMM_ACCESS_BARRED = 0x73;
+ /** Emergency interface only. */
+ public static final int EMERGENCY_IFACE_ONLY = 0x74;
+ /** Interface mismatch. */
+ public static final int IFACE_MISMATCH = 0x75;
+ /** Companion interface in use. */
+ public static final int COMPANION_IFACE_IN_USE = 0x76;
+ /** IP address mismatch. */
+ public static final int IP_ADDRESS_MISMATCH = 0x77;
+ public static final int IFACE_AND_POL_FAMILY_MISMATCH = 0x78;
+ /** EPS (Evolved Packet System) Mobility Management (EMM) access barred infinity retry. **/
+ public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79;
+ /** Authentication failure on emergency call. */
+ public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A;
// OEM sepecific error codes. To be used by OEMs when they don't
// want to reveal error code which would be replaced by ERROR_UNSPECIFIED
- OEM_DCFAILCAUSE_1(0x1001),
- OEM_DCFAILCAUSE_2(0x1002),
- OEM_DCFAILCAUSE_3(0x1003),
- OEM_DCFAILCAUSE_4(0x1004),
- OEM_DCFAILCAUSE_5(0x1005),
- OEM_DCFAILCAUSE_6(0x1006),
- OEM_DCFAILCAUSE_7(0x1007),
- OEM_DCFAILCAUSE_8(0x1008),
- OEM_DCFAILCAUSE_9(0x1009),
- OEM_DCFAILCAUSE_10(0x100A),
- OEM_DCFAILCAUSE_11(0x100B),
- OEM_DCFAILCAUSE_12(0x100C),
- OEM_DCFAILCAUSE_13(0x100D),
- OEM_DCFAILCAUSE_14(0x100E),
- OEM_DCFAILCAUSE_15(0x100F),
+ public static final int OEM_DCFAILCAUSE_1 = 0x1001;
+ public static final int OEM_DCFAILCAUSE_2 = 0x1002;
+ public static final int OEM_DCFAILCAUSE_3 = 0x1003;
+ public static final int OEM_DCFAILCAUSE_4 = 0x1004;
+ public static final int OEM_DCFAILCAUSE_5 = 0x1005;
+ public static final int OEM_DCFAILCAUSE_6 = 0x1006;
+ public static final int OEM_DCFAILCAUSE_7 = 0x1007;
+ public static final int OEM_DCFAILCAUSE_8 = 0x1008;
+ public static final int OEM_DCFAILCAUSE_9 = 0x1009;
+ public static final int OEM_DCFAILCAUSE_10 = 0x100A;
+ public static final int OEM_DCFAILCAUSE_11 = 0x100B;
+ public static final int OEM_DCFAILCAUSE_12 = 0x100C;
+ public static final int OEM_DCFAILCAUSE_13 = 0x100D;
+ public static final int OEM_DCFAILCAUSE_14 = 0x100E;
+ public static final int OEM_DCFAILCAUSE_15 = 0x100F;
// Local errors generated by Vendor RIL
// specified in ril.h
- REGISTRATION_FAIL(-1),
- GPRS_REGISTRATION_FAIL(-2),
- SIGNAL_LOST(-3), /* no retry */
- PREF_RADIO_TECH_CHANGED(-4),
- RADIO_POWER_OFF(-5), /* no retry */
- TETHERED_CALL_ACTIVE(-6), /* no retry */
- ERROR_UNSPECIFIED(0xFFFF),
+ public static final int REGISTRATION_FAIL = -1;
+ public static final int GPRS_REGISTRATION_FAIL = -2;
+ public static final int SIGNAL_LOST = -3; /* no retry */
+ public static final int PREF_RADIO_TECH_CHANGED = -4;
+ public static final int RADIO_POWER_OFF = -5; /* no retry */
+ public static final int TETHERED_CALL_ACTIVE = -6; /* no retry */
+ public static final int ERROR_UNSPECIFIED = 0xFFFF;
// Errors generated by the Framework
// specified here
- UNKNOWN(0x10000),
- RADIO_NOT_AVAILABLE(0x10001), /* no retry */
- UNACCEPTABLE_NETWORK_PARAMETER(0x10002), /* no retry */
- CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003),
- LOST_CONNECTION(0x10004),
- RESET_BY_FRAMEWORK(0x10005);
+ public static final int UNKNOWN = 0x10000;
+ public static final int RADIO_NOT_AVAILABLE = 0x10001; /* no retry */
+ public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002; /* no retry */
+ public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
+ public static final int LOST_CONNECTION = 0x10004;
+ /** Data was reset by framework. */
+ public static final int RESET_BY_FRAMEWORK = 0x10005;
- private final int mErrorCode;
- private static final HashMap<Integer, DataFailCause> sErrorCodeToFailCauseMap;
+ /** @hide */
+ @IntDef(value = {
+ NONE,
+ OPERATOR_BARRED,
+ NAS_SIGNALLING,
+ LLC_SNDCP,
+ INSUFFICIENT_RESOURCES,
+ MISSING_UNKNOWN_APN,
+ UNKNOWN_PDP_ADDRESS_TYPE,
+ USER_AUTHENTICATION,
+ ACTIVATION_REJECT_GGSN,
+ ACTIVATION_REJECT_UNSPECIFIED,
+ SERVICE_OPTION_NOT_SUPPORTED,
+ SERVICE_OPTION_NOT_SUBSCRIBED,
+ SERVICE_OPTION_OUT_OF_ORDER,
+ NSAPI_IN_USE,
+ REGULAR_DEACTIVATION,
+ QOS_NOT_ACCEPTED,
+ NETWORK_FAILURE,
+ UMTS_REACTIVATION_REQ,
+ FEATURE_NOT_SUPP,
+ TFT_SEMANTIC_ERROR,
+ TFT_SYTAX_ERROR,
+ UNKNOWN_PDP_CONTEXT,
+ FILTER_SEMANTIC_ERROR,
+ FILTER_SYTAX_ERROR,
+ PDP_WITHOUT_ACTIVE_TFT,
+ ONLY_IPV4_ALLOWED,
+ ONLY_IPV6_ALLOWED,
+ ONLY_SINGLE_BEARER_ALLOWED,
+ ESM_INFO_NOT_RECEIVED,
+ PDN_CONN_DOES_NOT_EXIST,
+ MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ MAX_ACTIVE_PDP_CONTEXT_REACHED,
+ UNSUPPORTED_APN_IN_CURRENT_PLMN,
+ INVALID_TRANSACTION_ID,
+ MESSAGE_INCORRECT_SEMANTIC,
+ INVALID_MANDATORY_INFO,
+ MESSAGE_TYPE_UNSUPPORTED,
+ MSG_TYPE_NONCOMPATIBLE_STATE,
+ UNKNOWN_INFO_ELEMENT,
+ CONDITIONAL_IE_ERROR,
+ MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+ PROTOCOL_ERRORS, /* no retry */
+ APN_TYPE_CONFLICT,
+ INVALID_PCSCF_ADDR,
+ INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+ EMM_ACCESS_BARRED,
+ EMERGENCY_IFACE_ONLY,
+ IFACE_MISMATCH,
+ COMPANION_IFACE_IN_USE,
+ IP_ADDRESS_MISMATCH,
+ IFACE_AND_POL_FAMILY_MISMATCH,
+ EMM_ACCESS_BARRED_INFINITE_RETRY,
+ AUTH_FAILURE_ON_EMERGENCY_CALL,
+ OEM_DCFAILCAUSE_1,
+ OEM_DCFAILCAUSE_2,
+ OEM_DCFAILCAUSE_3,
+ OEM_DCFAILCAUSE_4,
+ OEM_DCFAILCAUSE_5,
+ OEM_DCFAILCAUSE_6,
+ OEM_DCFAILCAUSE_7,
+ OEM_DCFAILCAUSE_8,
+ OEM_DCFAILCAUSE_9,
+ OEM_DCFAILCAUSE_10,
+ OEM_DCFAILCAUSE_11,
+ OEM_DCFAILCAUSE_12,
+ OEM_DCFAILCAUSE_13,
+ OEM_DCFAILCAUSE_14,
+ OEM_DCFAILCAUSE_15,
+ REGISTRATION_FAIL,
+ GPRS_REGISTRATION_FAIL,
+ SIGNAL_LOST,
+ PREF_RADIO_TECH_CHANGED,
+ RADIO_POWER_OFF,
+ TETHERED_CALL_ACTIVE,
+ ERROR_UNSPECIFIED,
+ UNKNOWN,
+ RADIO_NOT_AVAILABLE,
+ UNACCEPTABLE_NETWORK_PARAMETER,
+ CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+ LOST_CONNECTION,
+ RESET_BY_FRAMEWORK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FailCause{}
+
+ private static final Map<Integer, String> sFailCauseMap;
static {
- sErrorCodeToFailCauseMap = new HashMap<Integer, DataFailCause>();
- for (DataFailCause fc : values()) {
- sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
- }
+ sFailCauseMap = new HashMap<>();
+ sFailCauseMap.put(NONE, "NONE");
+ sFailCauseMap.put(OPERATOR_BARRED, "OPERATOR_BARRED");
+ sFailCauseMap.put(NAS_SIGNALLING, "NAS_SIGNALLING");
+ sFailCauseMap.put(LLC_SNDCP, "LLC_SNDCP");
+ sFailCauseMap.put(INSUFFICIENT_RESOURCES, "INSUFFICIENT_RESOURCES");
+ sFailCauseMap.put(MISSING_UNKNOWN_APN, "MISSING_UNKNOWN_APN");
+ sFailCauseMap.put(UNKNOWN_PDP_ADDRESS_TYPE, "UNKNOWN_PDP_ADDRESS_TYPE");
+ sFailCauseMap.put(USER_AUTHENTICATION, "USER_AUTHENTICATION");
+ sFailCauseMap.put(ACTIVATION_REJECT_GGSN, "ACTIVATION_REJECT_GGSN");
+ sFailCauseMap.put(ACTIVATION_REJECT_UNSPECIFIED,
+ "ACTIVATION_REJECT_UNSPECIFIED");
+ sFailCauseMap.put(SERVICE_OPTION_NOT_SUPPORTED,
+ "SERVICE_OPTION_NOT_SUPPORTED");
+ sFailCauseMap.put(SERVICE_OPTION_NOT_SUBSCRIBED,
+ "SERVICE_OPTION_NOT_SUBSCRIBED");
+ sFailCauseMap.put(SERVICE_OPTION_OUT_OF_ORDER, "SERVICE_OPTION_OUT_OF_ORDER");
+ sFailCauseMap.put(NSAPI_IN_USE, "NSAPI_IN_USE");
+ sFailCauseMap.put(REGULAR_DEACTIVATION, "REGULAR_DEACTIVATION");
+ sFailCauseMap.put(QOS_NOT_ACCEPTED, "QOS_NOT_ACCEPTED");
+ sFailCauseMap.put(NETWORK_FAILURE, "NETWORK_FAILURE");
+ sFailCauseMap.put(UMTS_REACTIVATION_REQ, "UMTS_REACTIVATION_REQ");
+ sFailCauseMap.put(FEATURE_NOT_SUPP, "FEATURE_NOT_SUPP");
+ sFailCauseMap.put(TFT_SEMANTIC_ERROR, "TFT_SEMANTIC_ERROR");
+ sFailCauseMap.put(TFT_SYTAX_ERROR, "TFT_SYTAX_ERROR");
+ sFailCauseMap.put(UNKNOWN_PDP_CONTEXT, "UNKNOWN_PDP_CONTEXT");
+ sFailCauseMap.put(FILTER_SEMANTIC_ERROR, "FILTER_SEMANTIC_ERROR");
+ sFailCauseMap.put(FILTER_SYTAX_ERROR, "FILTER_SYTAX_ERROR");
+ sFailCauseMap.put(PDP_WITHOUT_ACTIVE_TFT, "PDP_WITHOUT_ACTIVE_TFT");
+ sFailCauseMap.put(ONLY_IPV4_ALLOWED, "ONLY_IPV4_ALLOWED");
+ sFailCauseMap.put(ONLY_IPV6_ALLOWED, "ONLY_IPV6_ALLOWED");
+ sFailCauseMap.put(ONLY_SINGLE_BEARER_ALLOWED, "ONLY_SINGLE_BEARER_ALLOWED");
+ sFailCauseMap.put(ESM_INFO_NOT_RECEIVED, "ESM_INFO_NOT_RECEIVED");
+ sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
+ sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ "MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
+ sFailCauseMap.put(MAX_ACTIVE_PDP_CONTEXT_REACHED,
+ "MAX_ACTIVE_PDP_CONTEXT_REACHED");
+ sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
+ "UNSUPPORTED_APN_IN_CURRENT_PLMN");
+ sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID");
+ sFailCauseMap.put(MESSAGE_INCORRECT_SEMANTIC, "MESSAGE_INCORRECT_SEMANTIC");
+ sFailCauseMap.put(INVALID_MANDATORY_INFO, "INVALID_MANDATORY_INFO");
+ sFailCauseMap.put(MESSAGE_TYPE_UNSUPPORTED, "MESSAGE_TYPE_UNSUPPORTED");
+ sFailCauseMap.put(MSG_TYPE_NONCOMPATIBLE_STATE, "MSG_TYPE_NONCOMPATIBLE_STATE");
+ sFailCauseMap.put(UNKNOWN_INFO_ELEMENT, "UNKNOWN_INFO_ELEMENT");
+ sFailCauseMap.put(CONDITIONAL_IE_ERROR, "CONDITIONAL_IE_ERROR");
+ sFailCauseMap.put(MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+ "MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE");
+ sFailCauseMap.put(PROTOCOL_ERRORS, "PROTOCOL_ERRORS");
+ sFailCauseMap.put(APN_TYPE_CONFLICT, "APN_TYPE_CONFLICT");
+ sFailCauseMap.put(INVALID_PCSCF_ADDR, "INVALID_PCSCF_ADDR");
+ sFailCauseMap.put(INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+ "INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN");
+ sFailCauseMap.put(EMM_ACCESS_BARRED, "EMM_ACCESS_BARRED");
+ sFailCauseMap.put(EMERGENCY_IFACE_ONLY, "EMERGENCY_IFACE_ONLY");
+ sFailCauseMap.put(IFACE_MISMATCH, "IFACE_MISMATCH");
+ sFailCauseMap.put(COMPANION_IFACE_IN_USE, "COMPANION_IFACE_IN_USE");
+ sFailCauseMap.put(IP_ADDRESS_MISMATCH, "IP_ADDRESS_MISMATCH");
+ sFailCauseMap.put(IFACE_AND_POL_FAMILY_MISMATCH,
+ "IFACE_AND_POL_FAMILY_MISMATCH");
+ sFailCauseMap.put(EMM_ACCESS_BARRED_INFINITE_RETRY,
+ "EMM_ACCESS_BARRED_INFINITE_RETRY");
+ sFailCauseMap.put(AUTH_FAILURE_ON_EMERGENCY_CALL,
+ "AUTH_FAILURE_ON_EMERGENCY_CALL");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_4, "OEM_DCFAILCAUSE_4");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_5, "OEM_DCFAILCAUSE_5");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_6, "OEM_DCFAILCAUSE_6");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_7, "OEM_DCFAILCAUSE_7");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_8, "OEM_DCFAILCAUSE_8");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_9, "OEM_DCFAILCAUSE_9");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_10, "OEM_DCFAILCAUSE_10");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_11, "OEM_DCFAILCAUSE_11");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_12, "OEM_DCFAILCAUSE_12");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_13, "OEM_DCFAILCAUSE_13");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_14, "OEM_DCFAILCAUSE_14");
+ sFailCauseMap.put(OEM_DCFAILCAUSE_15, "OEM_DCFAILCAUSE_15");
+ sFailCauseMap.put(REGISTRATION_FAIL, "REGISTRATION_FAIL");
+ sFailCauseMap.put(GPRS_REGISTRATION_FAIL, "GPRS_REGISTRATION_FAIL");
+ sFailCauseMap.put(SIGNAL_LOST, "SIGNAL_LOST");
+ sFailCauseMap.put(PREF_RADIO_TECH_CHANGED, "PREF_RADIO_TECH_CHANGED");
+ sFailCauseMap.put(RADIO_POWER_OFF, "RADIO_POWER_OFF");
+ sFailCauseMap.put(TETHERED_CALL_ACTIVE, "TETHERED_CALL_ACTIVE");
+ sFailCauseMap.put(ERROR_UNSPECIFIED, "ERROR_UNSPECIFIED");
+ sFailCauseMap.put(UNKNOWN, "UNKNOWN");
+ sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE");
+ sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
+ "UNACCEPTABLE_NETWORK_PARAMETER");
+ sFailCauseMap.put(CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+ "CONNECTION_TO_DATACONNECTIONAC_BROKEN");
+ sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
+ sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
}
/**
* Map of subId -> set of data call setup permanent failure for the carrier.
*/
- private static final HashMap<Integer, HashSet<DataFailCause>> sPermanentFailureCache =
+ private static final HashMap<Integer, Set<Integer>> sPermanentFailureCache =
new HashMap<>();
- DataFailCause(int errorCode) {
- mErrorCode = errorCode;
- }
-
- public int getErrorCode() {
- return mErrorCode;
- }
-
/**
* Returns whether or not the fail cause is a failure that requires a modem restart
*
* @param context device context
+ * @param cause data disconnect cause
* @param subId subscription index
* @return true if the fail cause code needs platform to trigger a modem restart.
*/
- public boolean isRadioRestartFailure(Context context, int subId) {
+ public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause,
+ int subId) {
CarrierConfigManager configManager = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager != null) {
PersistableBundle b = configManager.getConfigForSubId(subId);
if (b != null) {
- if (this == REGULAR_DEACTIVATION
+ if (cause == REGULAR_DEACTIVATION
&& b.getBoolean(CarrierConfigManager
.KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) {
// This is for backward compatibility support. We need to continue support this
@@ -170,7 +402,7 @@
int[] causeCodes = b.getIntArray(CarrierConfigManager
.KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY);
if (causeCodes != null) {
- return Arrays.stream(causeCodes).anyMatch(i -> i == getErrorCode());
+ return Arrays.stream(causeCodes).anyMatch(i -> i == cause);
}
}
}
@@ -178,11 +410,11 @@
return false;
}
- public boolean isPermanentFailure(Context context, int subId) {
-
+ public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause,
+ int subId) {
synchronized (sPermanentFailureCache) {
- HashSet<DataFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+ Set<Integer> permanentFailureSet = sPermanentFailureCache.get(subId);
// In case of cache miss, we need to look up the settings from carrier config.
if (permanentFailureSet == null) {
@@ -194,11 +426,12 @@
if (b != null) {
String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
-
if (permanentFailureStrings != null) {
permanentFailureSet = new HashSet<>();
- for (String failure : permanentFailureStrings) {
- permanentFailureSet.add(DataFailCause.valueOf(failure));
+ for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) {
+ if (ArrayUtils.contains(permanentFailureStrings, e.getValue())) {
+ permanentFailureSet.add(e.getKey());
+ }
}
}
}
@@ -207,7 +440,7 @@
// If we are not able to find the configuration from carrier config, use the default
// ones.
if (permanentFailureSet == null) {
- permanentFailureSet = new HashSet<DataFailCause>() {
+ permanentFailureSet = new HashSet<Integer>() {
{
add(OPERATOR_BARRED);
add(MISSING_UNKNOWN_APN);
@@ -232,28 +465,39 @@
sPermanentFailureCache.put(subId, permanentFailureSet);
}
- return permanentFailureSet.contains(this);
+ return permanentFailureSet.contains(failCause);
}
}
- public boolean isEventLoggable() {
- return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
- (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
- (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
- (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
- (this == SERVICE_OPTION_NOT_SUPPORTED) ||
- (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
- (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
- (this == PROTOCOL_ERRORS) || (this == SIGNAL_LOST) ||
- (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
- (this == UNACCEPTABLE_NETWORK_PARAMETER);
+ public static boolean isEventLoggable(@FailCause int dataFailCause) {
+ return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
+ || (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
+ || (dataFailCause == USER_AUTHENTICATION)
+ || (dataFailCause == ACTIVATION_REJECT_GGSN)
+ || (dataFailCause == ACTIVATION_REJECT_UNSPECIFIED)
+ || (dataFailCause == SERVICE_OPTION_NOT_SUBSCRIBED)
+ || (dataFailCause == SERVICE_OPTION_NOT_SUPPORTED)
+ || (dataFailCause == SERVICE_OPTION_OUT_OF_ORDER)
+ || (dataFailCause == NSAPI_IN_USE)
+ || (dataFailCause == ONLY_IPV4_ALLOWED)
+ || (dataFailCause == ONLY_IPV6_ALLOWED)
+ || (dataFailCause == PROTOCOL_ERRORS)
+ || (dataFailCause == SIGNAL_LOST)
+ || (dataFailCause == RADIO_POWER_OFF)
+ || (dataFailCause == TETHERED_CALL_ACTIVE)
+ || (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER);
}
- public static DataFailCause fromInt(int errorCode) {
- DataFailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
- if (fc == null) {
- fc = UNKNOWN;
+ public static String toString(@FailCause int dataFailCause) {
+ int cause = getFailCause(dataFailCause);
+ return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause);
+ }
+
+ public static int getFailCause(@FailCause int failCause) {
+ if (sFailCauseMap.containsKey(failCause)) {
+ return failCause;
+ } else {
+ return UNKNOWN;
}
- return fc;
}
}
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 4482074..c4b70ab 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -56,6 +56,9 @@
public static final int RAF_LTE = TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
public static final int RAF_LTE_CA = TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
+ // 5G
+ public static final int RAF_NR = TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+
// Grouping of RAFs
// 2G
private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
@@ -67,6 +70,9 @@
// 4G
private static final int LTE = RAF_LTE | RAF_LTE_CA;
+ // 5G
+ private static final int NR = RAF_NR;
+
/* Phone ID of phone */
private int mPhoneId;
@@ -159,84 +165,78 @@
@UnsupportedAppUsage
public static int getRafFromNetworkType(int type) {
- int raf;
-
switch (type) {
case RILConstants.NETWORK_MODE_WCDMA_PREF:
- raf = GSM | WCDMA;
- break;
+ return GSM | WCDMA;
case RILConstants.NETWORK_MODE_GSM_ONLY:
- raf = GSM;
- break;
+ return GSM;
case RILConstants.NETWORK_MODE_WCDMA_ONLY:
- raf = WCDMA;
- break;
+ return WCDMA;
case RILConstants.NETWORK_MODE_GSM_UMTS:
- raf = GSM | WCDMA;
- break;
+ return GSM | WCDMA;
case RILConstants.NETWORK_MODE_CDMA:
- raf = CDMA | EVDO;
- break;
+ return CDMA | EVDO;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
- raf = LTE | CDMA | EVDO;
- break;
+ return LTE | CDMA | EVDO;
case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
- raf = LTE | GSM | WCDMA;
- break;
+ return LTE | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
- raf = LTE | CDMA | EVDO | GSM | WCDMA;
- break;
+ return LTE | CDMA | EVDO | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_ONLY:
- raf = LTE;
- break;
+ return LTE;
case RILConstants.NETWORK_MODE_LTE_WCDMA:
- raf = LTE | WCDMA;
- break;
+ return LTE | WCDMA;
case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
- raf = CDMA;
- break;
+ return CDMA;
case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
- raf = EVDO;
- break;
+ return EVDO;
case RILConstants.NETWORK_MODE_GLOBAL:
- raf = GSM | WCDMA | CDMA | EVDO;
- break;
+ return GSM | WCDMA | CDMA | EVDO;
case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
- raf = RAF_TD_SCDMA;
- break;
+ return RAF_TD_SCDMA;
case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
- raf = RAF_TD_SCDMA | WCDMA;
- break;
+ return RAF_TD_SCDMA | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
- raf = LTE | RAF_TD_SCDMA;
- break;
+ return LTE | RAF_TD_SCDMA;
case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
- raf = RAF_TD_SCDMA | GSM;
- break;
+ return RAF_TD_SCDMA | GSM;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
- raf = LTE | RAF_TD_SCDMA | GSM;
- break;
+ return LTE | RAF_TD_SCDMA | GSM;
case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
- raf = RAF_TD_SCDMA | GSM | WCDMA;
- break;
+ return RAF_TD_SCDMA | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
- raf = LTE | RAF_TD_SCDMA | WCDMA;
- break;
+ return LTE | RAF_TD_SCDMA | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
- raf = LTE | RAF_TD_SCDMA | GSM | WCDMA;
- break;
+ return LTE | RAF_TD_SCDMA | GSM | WCDMA;
case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
- raf = RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
- break;
+ return RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
- raf = LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
- break;
+ return LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_ONLY):
+ return NR;
+ case (RILConstants.NETWORK_MODE_NR_LTE):
+ return NR | LTE;
+ case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO):
+ return NR | LTE | CDMA | EVDO;
+ case (RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA):
+ return NR | LTE | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA):
+ return NR | LTE | CDMA | EVDO | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_WCDMA):
+ return NR | LTE | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA):
+ return NR | LTE | RAF_TD_SCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM):
+ return NR | LTE | RAF_TD_SCDMA | GSM;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA):
+ return NR | LTE | RAF_TD_SCDMA | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA):
+ return NR | LTE | RAF_TD_SCDMA | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA):
+ return NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
default:
- raf = RAF_UNKNOWN;
- break;
+ return RAF_UNKNOWN;
}
-
- return raf;
}
/**
@@ -249,6 +249,7 @@
raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
+ raf = ((NR & raf) > 0) ? (NR | raf) : raf;
return raf;
}
@@ -273,83 +274,78 @@
@UnsupportedAppUsage
public static int getNetworkTypeFromRaf(int raf) {
- int type;
-
raf = getAdjustedRaf(raf);
switch (raf) {
case (GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_WCDMA_PREF;
- break;
+ return RILConstants.NETWORK_MODE_WCDMA_PREF;
case GSM:
- type = RILConstants.NETWORK_MODE_GSM_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_GSM_ONLY;
case WCDMA:
- type = RILConstants.NETWORK_MODE_WCDMA_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_WCDMA_ONLY;
case (CDMA | EVDO):
- type = RILConstants.NETWORK_MODE_CDMA;
- break;
+ return RILConstants.NETWORK_MODE_CDMA;
case (LTE | CDMA | EVDO):
- type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
- break;
+ return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
case (LTE | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
case (LTE | CDMA | EVDO | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
case LTE:
- type = RILConstants.NETWORK_MODE_LTE_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_LTE_ONLY;
case (LTE | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_WCDMA;
case CDMA:
- type = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
- break;
+ return RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
case EVDO:
- type = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
- break;
+ return RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
case (GSM | WCDMA | CDMA | EVDO):
- type = RILConstants.NETWORK_MODE_GLOBAL;
- break;
+ return RILConstants.NETWORK_MODE_GLOBAL;
case RAF_TD_SCDMA:
- type = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
case (RAF_TD_SCDMA | WCDMA):
- type = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
case (LTE | RAF_TD_SCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA;
case (RAF_TD_SCDMA | GSM):
- type = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_GSM;
case (LTE | RAF_TD_SCDMA | GSM):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
case (RAF_TD_SCDMA | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
case (LTE | RAF_TD_SCDMA | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
case (LTE | RAF_TD_SCDMA | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
case (RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
case (LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+ case (NR):
+ return RILConstants.NETWORK_MODE_NR_ONLY;
+ case (NR | LTE):
+ return RILConstants.NETWORK_MODE_NR_LTE;
+ case (NR | LTE | CDMA | EVDO):
+ return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+ case (NR | LTE | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+ case (NR | LTE | CDMA | EVDO | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+ case (NR | LTE | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+ case (NR | LTE | RAF_TD_SCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+ case (NR | LTE | RAF_TD_SCDMA | GSM):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+ case (NR | LTE | RAF_TD_SCDMA | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+ case (NR | LTE | RAF_TD_SCDMA | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+ case (NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
default:
- type = RILConstants.PREFERRED_NETWORK_MODE ;
- break;
+ return RILConstants.PREFERRED_NETWORK_MODE;
}
-
- return type;
}
public static int singleRafTypeFromString(String rafString) {
@@ -376,6 +372,7 @@
case "EVDO": return EVDO;
case "WCDMA": return WCDMA;
case "LTE_CA": return RAF_LTE_CA;
+ case "NR": return RAF_NR;
default: return RAF_UNKNOWN;
}
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 240b8a9..ef185c5 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -16,16 +16,14 @@
package android.telephony;
+import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.CarrierConfigManager;
-import android.util.Log;
+import android.os.PersistableBundle;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Objects;
/**
@@ -59,6 +57,8 @@
/** @hide */
@UnsupportedAppUsage
public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
+
+ /** SIGNAL_STRENGTH_NAMES is currently used by BatteryStats, but to-be-removed soon. */
/** @hide */
public static final String[] SIGNAL_STRENGTH_NAMES = {
"none", "poor", "moderate", "good", "great"
@@ -72,66 +72,22 @@
public static final int INVALID = Integer.MAX_VALUE;
private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
- private static final int MAX_LTE_RSRP = -44;
- private static final int MIN_LTE_RSRP = -140;
private static final int WCDMA_RSCP_THRESHOLDS_NUM = 4;
- private static final int MAX_WCDMA_RSCP = -24;
- private static final int MIN_WCDMA_RSCP = -120;
/* The type of signal measurement */
- private static final String MEASUMENT_TYPE_RSCP = "rscp";
+ private static final String MEASUREMENT_TYPE_RSCP = "rscp";
- /** Parameters reported by the Radio */
- @UnsupportedAppUsage
- private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
- @UnsupportedAppUsage
- private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- @UnsupportedAppUsage
- private int mCdmaDbm; // This value is the RSSI value
- @UnsupportedAppUsage
- private int mCdmaEcio; // This value is the Ec/Io
- @UnsupportedAppUsage
- private int mEvdoDbm; // This value is the EVDO RSSI value
- @UnsupportedAppUsage
- private int mEvdoEcio; // This value is the EVDO Ec/Io
- @UnsupportedAppUsage
- private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
- @UnsupportedAppUsage
- private int mLteSignalStrength;
- @UnsupportedAppUsage
- private int mLteRsrp;
- @UnsupportedAppUsage
- private int mLteRsrq;
- @UnsupportedAppUsage
- private int mLteRssnr;
- @UnsupportedAppUsage
- private int mLteCqi;
- @UnsupportedAppUsage
- private int mTdScdmaRscp; // Valid values are -24...-120dBm or INVALID if unknown
- private int mWcdmaSignalStrength;
- private int mWcdmaRscpAsu; // the WCDMA RSCP in ASU as reported from the HAL
- @UnsupportedAppUsage
- private int mWcdmaRscp; // the WCDMA RSCP in dBm
+ CellSignalStrengthCdma mCdma;
+ CellSignalStrengthGsm mGsm;
+ CellSignalStrengthWcdma mWcdma;
+ CellSignalStrengthTdscdma mTdscdma;
+ CellSignalStrengthLte mLte;
/** Parameters from the framework */
@UnsupportedAppUsage
private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
// signal strength level
- private boolean mIsGsm; // This value is set by the ServiceStateTracker
- // onSignalStrengthResult.
- private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
-
- // The threshold of LTE RSRP for determining the display level of LTE signal bar. Note that the
- // min and max are fixed at MIN_LTE_RSRP (-140) and MAX_LTE_RSRP (-44).
- private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
-
- // The type of default measurement for determining the display level of WCDMA signal bar.
- private String mWcdmaDefaultSignalMeasurement;
-
- // The threshold of WCDMA RSCP for determining the display level of WCDMA signal bar. Note that
- // the min and max are fixed at MIN_WCDMA_RSCP (-120) and MAX_WCDMA_RSCP (-24).
- private int mWcdmaRscpThresholds[] = new int[WCDMA_RSCP_THRESHOLDS_NUM];
/**
* Create a new SignalStrength from a intent notifier Bundle
@@ -153,47 +109,17 @@
}
/**
- * Empty constructor
- *
- * @hide
- */
- @UnsupportedAppUsage
- public SignalStrength() {
- this(true);
- }
-
- /**
* This constructor is used to create SignalStrength with default
- * values and set the gsmFlag with the value passed in the input
+ * values.
*
- * @param gsmFlag true if Gsm Phone,false if Cdma phone
* @return newly created SignalStrength
* @hide
*/
@UnsupportedAppUsage
- public SignalStrength(boolean gsmFlag) {
- mGsmSignalStrength = 99;
- mGsmBitErrorRate = -1;
- mCdmaDbm = INVALID;
- mCdmaEcio = -1;
- mEvdoDbm = INVALID;
- mEvdoEcio = -1;
- mEvdoSnr = -1;
- mLteSignalStrength = 99;
- mLteRsrp = INVALID;
- mLteRsrq = INVALID;
- mLteRssnr = INVALID;
- mLteCqi = INVALID;
- mTdScdmaRscp = INVALID;
- mWcdmaSignalStrength = 99;
- mWcdmaRscp = INVALID;
- mWcdmaRscpAsu = 255;
- mLteRsrpBoost = 0;
- mIsGsm = gsmFlag;
- mUseOnlyRsrpForLteLevel = false;
- mWcdmaDefaultSignalMeasurement = "";
- setLteRsrpThresholds(getDefaultLteRsrpThresholds());
- setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
+ public SignalStrength() {
+ this(new CellSignalStrengthCdma(), new CellSignalStrengthGsm(),
+ new CellSignalStrengthWcdma(), new CellSignalStrengthTdscdma(),
+ new CellSignalStrengthLte());
}
/**
@@ -202,68 +128,64 @@
* @hide
*/
public SignalStrength(
- int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscpAsu,
- // values Added by config
- int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp,
- String wcdmaDefaultMeasurement) {
- mGsmSignalStrength = gsmSignalStrength;
- mGsmBitErrorRate = gsmBitErrorRate;
- mCdmaDbm = cdmaDbm;
- mCdmaEcio = cdmaEcio;
- mEvdoDbm = evdoDbm;
- mEvdoEcio = evdoEcio;
- mEvdoSnr = evdoSnr;
- mLteSignalStrength = lteSignalStrength;
- mLteRsrp = lteRsrp;
- mLteRsrq = lteRsrq;
- mLteRssnr = lteRssnr;
- mLteCqi = lteCqi;
- mTdScdmaRscp = INVALID;
- mWcdmaSignalStrength = wcdmaSignalStrength;
- mWcdmaRscpAsu = wcdmaRscpAsu;
- mWcdmaRscp = wcdmaRscpAsu - 120;
- mLteRsrpBoost = lteRsrpBoost;
- mIsGsm = gsmFlag;
- mUseOnlyRsrpForLteLevel = lteLevelBaseOnRsrp;
- mWcdmaDefaultSignalMeasurement = wcdmaDefaultMeasurement;
- setLteRsrpThresholds(getDefaultLteRsrpThresholds());
- setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
- if (DBG) log("initialize: " + toString());
+ @NonNull CellSignalStrengthCdma cdma,
+ @NonNull CellSignalStrengthGsm gsm,
+ @NonNull CellSignalStrengthWcdma wcdma,
+ @NonNull CellSignalStrengthTdscdma tdscdma,
+ @NonNull CellSignalStrengthLte lte) {
+ mCdma = cdma;
+ mGsm = gsm;
+ mWcdma = wcdma;
+ mTdscdma = tdscdma;
+ mLte = lte;
+ mLteRsrpBoost = 0;
}
/**
- * Constructor for only values provided by Radio HAL V1.0
+ * Constructor for Radio HAL V1.0
*
* @hide
*/
- public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int tdScdmaRscp) {
- this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 99, INVALID, 0, true, false, "");
+ public SignalStrength(android.hardware.radio.V1_0.SignalStrength signalStrength) {
+ this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo),
+ new CellSignalStrengthGsm(signalStrength.gw),
+ new CellSignalStrengthWcdma(),
+ new CellSignalStrengthTdscdma(signalStrength.tdScdma),
+ new CellSignalStrengthLte(signalStrength.lte));
}
/**
- * Constructor for only values provided by Radio HAL V1.2
+ * Constructor for Radio HAL V1.2
*
* @hide
*/
- public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscp) {
- this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, wcdmaSignalStrength, wcdmaRscp, 0, true,
- false, "");
+ public SignalStrength(android.hardware.radio.V1_2.SignalStrength signalStrength) {
+ this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo),
+ new CellSignalStrengthGsm(signalStrength.gsm),
+ new CellSignalStrengthWcdma(signalStrength.wcdma),
+ new CellSignalStrengthTdscdma(signalStrength.tdScdma),
+ new CellSignalStrengthLte(signalStrength.lte));
+ }
+
+ private CellSignalStrength getPrimary() {
+ // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing
+ // newer faster RATs for default/for display purposes.
+ if (mLte.isValid()) return mLte;
+ if (mCdma.isValid()) return mCdma;
+ if (mTdscdma.isValid()) return mTdscdma;
+ if (mWcdma.isValid()) return mWcdma;
+ if (mGsm.isValid()) return mGsm;
+ return mLte;
+ }
+
+ /** @hide */
+ public void updateLevel(PersistableBundle cc, ServiceState ss) {
+ mLteRsrpBoost = ss.getLteEarfcnRsrpBoost();
+ mCdma.updateLevel(cc, ss);
+ mGsm.updateLevel(cc, ss);
+ mWcdma.updateLevel(cc, ss);
+ mTdscdma.updateLevel(cc, ss);
+ mLte.updateLevel(cc, ss);
}
/**
@@ -283,28 +205,12 @@
*/
@UnsupportedAppUsage
protected void copyFrom(SignalStrength s) {
- mGsmSignalStrength = s.mGsmSignalStrength;
- mGsmBitErrorRate = s.mGsmBitErrorRate;
- mCdmaDbm = s.mCdmaDbm;
- mCdmaEcio = s.mCdmaEcio;
- mEvdoDbm = s.mEvdoDbm;
- mEvdoEcio = s.mEvdoEcio;
- mEvdoSnr = s.mEvdoSnr;
- mLteSignalStrength = s.mLteSignalStrength;
- mLteRsrp = s.mLteRsrp;
- mLteRsrq = s.mLteRsrq;
- mLteRssnr = s.mLteRssnr;
- mLteCqi = s.mLteCqi;
- mTdScdmaRscp = s.mTdScdmaRscp;
- mWcdmaSignalStrength = s.mWcdmaSignalStrength;
- mWcdmaRscpAsu = s.mWcdmaRscpAsu;
- mWcdmaRscp = s.mWcdmaRscp;
+ mCdma = new CellSignalStrengthCdma(s.mCdma);
+ mGsm = new CellSignalStrengthGsm(s.mGsm);
+ mWcdma = new CellSignalStrengthWcdma(s.mWcdma);
+ mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma);
+ mLte = new CellSignalStrengthLte(s.mLte);
mLteRsrpBoost = s.mLteRsrpBoost;
- mIsGsm = s.mIsGsm;
- mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
- mWcdmaDefaultSignalMeasurement = s.mWcdmaDefaultSignalMeasurement;
- setLteRsrpThresholds(s.mLteRsrpThresholds);
- setWcdmaRscpThresholds(s.mWcdmaRscpThresholds);
}
/**
@@ -316,56 +222,25 @@
public SignalStrength(Parcel in) {
if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
- mGsmSignalStrength = in.readInt();
- mGsmBitErrorRate = in.readInt();
- mCdmaDbm = in.readInt();
- mCdmaEcio = in.readInt();
- mEvdoDbm = in.readInt();
- mEvdoEcio = in.readInt();
- mEvdoSnr = in.readInt();
- mLteSignalStrength = in.readInt();
- mLteRsrp = in.readInt();
- mLteRsrq = in.readInt();
- mLteRssnr = in.readInt();
- mLteCqi = in.readInt();
- mTdScdmaRscp = in.readInt();
- mWcdmaSignalStrength = in.readInt();
- mWcdmaRscpAsu = in.readInt();
- mWcdmaRscp = in.readInt();
+ mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader());
+ mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader());
+ mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader());
+ mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader());
+ mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
mLteRsrpBoost = in.readInt();
- mIsGsm = in.readBoolean();
- mUseOnlyRsrpForLteLevel = in.readBoolean();
- mWcdmaDefaultSignalMeasurement = in.readString();
- in.readIntArray(mLteRsrpThresholds);
- in.readIntArray(mWcdmaRscpThresholds);
}
/**
* {@link Parcelable#writeToParcel}
*/
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mGsmSignalStrength);
- out.writeInt(mGsmBitErrorRate);
- out.writeInt(mCdmaDbm);
- out.writeInt(mCdmaEcio);
- out.writeInt(mEvdoDbm);
- out.writeInt(mEvdoEcio);
- out.writeInt(mEvdoSnr);
- out.writeInt(mLteSignalStrength);
- out.writeInt(mLteRsrp);
- out.writeInt(mLteRsrq);
- out.writeInt(mLteRssnr);
- out.writeInt(mLteCqi);
- out.writeInt(mTdScdmaRscp);
- out.writeInt(mWcdmaSignalStrength);
- out.writeInt(mWcdmaRscpAsu);
- out.writeInt(mWcdmaRscp);
+ out.writeParcelable(mCdma, flags);
+ out.writeParcelable(mGsm, flags);
+ out.writeParcelable(mWcdma, flags);
+ out.writeParcelable(mTdscdma, flags);
+ out.writeParcelable(mLte, flags);
+
out.writeInt(mLteRsrpBoost);
- out.writeBoolean(mIsGsm);
- out.writeBoolean(mUseOnlyRsrpForLteLevel);
- out.writeString(mWcdmaDefaultSignalMeasurement);
- out.writeIntArray(mLteRsrpThresholds);
- out.writeIntArray(mWcdmaRscpThresholds);
}
/**
@@ -392,153 +267,21 @@
};
/**
- * Validate the individual signal strength fields as per the range
- * specified in ril.h
- * Set to invalid any field that is not in the valid range
- * Cdma, evdo, lte rsrp & rsrq values are sign converted
- * when received from ril interface
+ * Get the GSM RSSI in ASU.
*
- * @return
- * Valid values for all signalstrength fields
- * @hide
- */
- @UnsupportedAppUsage
- public void validateInput() {
- if (DBG) log("Signal before validate=" + this);
- // TS 27.007 8.5
- mGsmSignalStrength = mGsmSignalStrength >= 0 ? mGsmSignalStrength : 99;
- mWcdmaSignalStrength = (mWcdmaSignalStrength >= 0) ? mWcdmaSignalStrength : 99;
- mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
- // BER no change;
-
- // WCDMA RSCP valid values are -120 through -24 as defined in TS 27.007 8.69
- // but are reported in ASU which is 0 through 96, so we do the conversion here
- mWcdmaRscpAsu =
- ((mWcdmaRscpAsu - 120 >= MIN_WCDMA_RSCP) && (mWcdmaRscpAsu - 120 <= MAX_WCDMA_RSCP))
- ? mWcdmaRscpAsu : 255;
- mWcdmaRscp = ((mWcdmaRscp >= MIN_WCDMA_RSCP) && (mWcdmaRscp <= MAX_WCDMA_RSCP))
- ? mWcdmaRscp : INVALID;
-
- mCdmaDbm = mCdmaDbm > 0 ? -mCdmaDbm : -120;
- mCdmaEcio = (mCdmaEcio >= 0) ? -mCdmaEcio : -160;
-
- mEvdoDbm = (mEvdoDbm > 0) ? -mEvdoDbm : -120;
- mEvdoEcio = (mEvdoEcio >= 0) ? -mEvdoEcio : -160;
- mEvdoSnr = ((mEvdoSnr >= 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
-
- // TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
- mLteRsrp = ((-mLteRsrp >= MIN_LTE_RSRP) && (-mLteRsrp <= MAX_LTE_RSRP)) ? -mLteRsrp
- : SignalStrength.INVALID;
- mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
- mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
- : SignalStrength.INVALID;
-
- mTdScdmaRscp = ((mTdScdmaRscp >= 0) && (mTdScdmaRscp <= 96))
- ? (mTdScdmaRscp - 120) : SignalStrength.INVALID;
- // Cqi no change
- if (DBG) log("Signal after validate=" + this);
- }
-
- /**
- * Fix {@link #mIsGsm} based on the signal strength data.
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
*
- * @hide
- */
- public void fixType() {
- mIsGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- }
-
- /**
- * @param true - Gsm, Lte phones
- * false - Cdma phones
- *
- * Used by voice phone to set the mIsGsm
- * flag
- * @hide
- */
- public void setGsm(boolean gsmFlag) {
- mIsGsm = gsmFlag;
- }
-
- /**
- * @param useOnlyRsrpForLteLevel true if it uses only RSRP for the number of LTE signal bar,
- * otherwise false.
- *
- * Used by phone to use only RSRP or not for the number of LTE signal bar.
- * @hide
- */
- public void setUseOnlyRsrpForLteLevel(boolean useOnlyRsrpForLteLevel) {
- mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
- }
-
- /**
- * @param defaultMeasurement sets the type of WCDMA default signal measurement
- *
- * Used by phone to determine default measurement type for calculation WCDMA signal level.
- * @hide
- */
- public void setWcdmaDefaultSignalMeasurement(String defaultMeasurement) {
- mWcdmaDefaultSignalMeasurement = defaultMeasurement;
- }
-
- /**
- * @param lteRsrpBoost - signal strength offset
- *
- * Used by phone to set the lte signal strength offset which will be
- * reduced from rsrp threshold while calculating signal strength level
- *
- * @hide
- */
- public void setLteRsrpBoost(int lteRsrpBoost) {
- mLteRsrpBoost = lteRsrpBoost;
- }
-
- /**
- * Sets the threshold array for determining the display level of LTE signal bar.
- *
- * @param lteRsrpThresholds int array for determining the display level.
- *
- * @hide
- */
- public void setLteRsrpThresholds(int[] lteRsrpThresholds) {
- if ((lteRsrpThresholds == null)
- || (lteRsrpThresholds.length != LTE_RSRP_THRESHOLDS_NUM)) {
- Log.wtf(LOG_TAG, "setLteRsrpThresholds - lteRsrpThresholds is invalid.");
- return;
- }
- System.arraycopy(lteRsrpThresholds, 0, mLteRsrpThresholds, 0, LTE_RSRP_THRESHOLDS_NUM);
- }
-
- /**
- * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
- * 27.007 8.5
+ * @return RSSI in ASU 0..31, 99, or UNAVAILABLE
*/
public int getGsmSignalStrength() {
- return this.mGsmSignalStrength;
+ return mGsm.getAsuLevel();
}
/**
* Get the GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5
*/
public int getGsmBitErrorRate() {
- return this.mGsmBitErrorRate;
- }
-
- /**
- * Sets the threshold array for determining the display level of WCDMA signal bar.
- *
- * @param wcdmaRscpThresholds int array for determining the display level.
- *
- * @hide
- */
- public void setWcdmaRscpThresholds(int[] wcdmaRscpThresholds) {
- if ((wcdmaRscpThresholds == null)
- || (wcdmaRscpThresholds.length != WCDMA_RSCP_THRESHOLDS_NUM)) {
- Log.wtf(LOG_TAG, "setWcdmaRscpThresholds - wcdmaRscpThresholds is invalid.");
- return;
- }
- System.arraycopy(wcdmaRscpThresholds, 0, mWcdmaRscpThresholds, 0,
- WCDMA_RSCP_THRESHOLDS_NUM);
+ return mGsm.getBitErrorRate();
}
/**
@@ -547,14 +290,14 @@
* @return the CDMA RSSI value or {@link #INVALID} if invalid
*/
public int getCdmaDbm() {
- return this.mCdmaDbm;
+ return mCdma.getCdmaDbm();
}
/**
* Get the CDMA Ec/Io value in dB*10
*/
public int getCdmaEcio() {
- return this.mCdmaEcio;
+ return mCdma.getCdmaEcio();
}
/**
@@ -563,51 +306,51 @@
* @return the EVDO RSSI value or {@link #INVALID} if invalid
*/
public int getEvdoDbm() {
- return this.mEvdoDbm;
+ return mCdma.getEvdoDbm();
}
/**
* Get the EVDO Ec/Io value in dB*10
*/
public int getEvdoEcio() {
- return this.mEvdoEcio;
+ return mCdma.getEvdoEcio();
}
/**
* Get the signal to noise ratio. Valid values are 0-8. 8 is the highest.
*/
public int getEvdoSnr() {
- return this.mEvdoSnr;
+ return mCdma.getEvdoSnr();
}
/** @hide */
@UnsupportedAppUsage
public int getLteSignalStrength() {
- return mLteSignalStrength;
+ return mLte.getRssi();
}
/** @hide */
@UnsupportedAppUsage
public int getLteRsrp() {
- return mLteRsrp;
+ return mLte.getRsrp();
}
/** @hide */
@UnsupportedAppUsage
public int getLteRsrq() {
- return mLteRsrq;
+ return mLte.getRsrq();
}
/** @hide */
@UnsupportedAppUsage
public int getLteRssnr() {
- return mLteRssnr;
+ return mLte.getRssnr();
}
/** @hide */
@UnsupportedAppUsage
public int getLteCqi() {
- return mLteCqi;
+ return mLte.getCqi();
}
/** @hide */
@@ -624,9 +367,12 @@
* while 4 represents a very strong signal strength.
*/
public int getLevel() {
- int level = mIsGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
- if (DBG) log("getLevel=" + level);
- return level;
+ int level = getPrimary().getLevel();
+ if (level < SIGNAL_STRENGTH_NONE_OR_UNKNOWN || level > SIGNAL_STRENGTH_GREAT) {
+ log("Invalid Level " + level + ", this=" + this);
+ return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ }
+ return getPrimary().getLevel();
}
/**
@@ -636,33 +382,7 @@
*/
@UnsupportedAppUsage
public int getAsuLevel() {
- int asuLevel = 0;
- if (mIsGsm) {
- if (mLteRsrp != SignalStrength.INVALID) {
- asuLevel = getLteAsuLevel();
- } else if (mTdScdmaRscp != SignalStrength.INVALID) {
- asuLevel = getTdScdmaAsuLevel();
- } else if (mWcdmaRscp != SignalStrength.INVALID) {
- asuLevel = getWcdmaAsuLevel();
- } else {
- asuLevel = getGsmAsuLevel();
- }
- } else {
- int cdmaAsuLevel = getCdmaAsuLevel();
- int evdoAsuLevel = getEvdoAsuLevel();
- if (evdoAsuLevel == 0) {
- /* We don't know evdo use, cdma */
- asuLevel = cdmaAsuLevel;
- } else if (cdmaAsuLevel == 0) {
- /* We don't know cdma use, evdo */
- asuLevel = evdoAsuLevel;
- } else {
- /* We know both, use the lowest level */
- asuLevel = cdmaAsuLevel < evdoAsuLevel ? cdmaAsuLevel : evdoAsuLevel;
- }
- }
- if (DBG) log("getAsuLevel=" + asuLevel);
- return asuLevel;
+ return getPrimary().getAsuLevel();
}
/**
@@ -672,30 +392,7 @@
*/
@UnsupportedAppUsage
public int getDbm() {
- int dBm = INVALID;
-
- if(isGsm()) {
- dBm = getLteDbm();
- if (dBm == INVALID) {
- if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- if (getWcdmaDbm() == INVALID) {
- dBm = getGsmDbm();
- } else {
- dBm = getWcdmaDbm();
- }
- } else {
- dBm = getTdScdmaDbm();
- }
- }
- } else {
- int cdmaDbm = getCdmaDbm();
- int evdoDbm = getEvdoDbm();
-
- return (evdoDbm == -120) ? cdmaDbm : ((cdmaDbm == -120) ? evdoDbm
- : (cdmaDbm < evdoDbm ? cdmaDbm : evdoDbm));
- }
- if (DBG) log("getDbm=" + dBm);
- return dBm;
+ return getPrimary().getDbm();
}
/**
@@ -705,17 +402,7 @@
*/
@UnsupportedAppUsage
public int getGsmDbm() {
- int dBm;
-
- int gsmSignalStrength = getGsmSignalStrength();
- int asu = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
- if (asu != -1) {
- dBm = -113 + (2 * asu);
- } else {
- dBm = -1;
- }
- if (DBG) log("getGsmDbm=" + dBm);
- return dBm;
+ return mGsm.getDbm();
}
/**
@@ -725,20 +412,7 @@
*/
@UnsupportedAppUsage
public int getGsmLevel() {
- int level;
-
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int asu = getGsmSignalStrength();
- if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (asu >= 12) level = SIGNAL_STRENGTH_GREAT;
- else if (asu >= 8) level = SIGNAL_STRENGTH_GOOD;
- else if (asu >= 5) level = SIGNAL_STRENGTH_MODERATE;
- else level = SIGNAL_STRENGTH_POOR;
- if (DBG) log("getGsmLevel=" + level);
- return level;
+ return mGsm.getLevel();
}
/**
@@ -748,13 +422,7 @@
*/
@UnsupportedAppUsage
public int getGsmAsuLevel() {
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- int level = getGsmSignalStrength();
- if (DBG) log("getGsmAsuLevel=" + level);
- return level;
+ return mGsm.getAsuLevel();
}
/**
@@ -764,27 +432,7 @@
*/
@UnsupportedAppUsage
public int getCdmaLevel() {
- final int cdmaDbm = getCdmaDbm();
- final int cdmaEcio = getCdmaEcio();
- int levelDbm;
- int levelEcio;
-
- if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
- else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
- else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
- else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
- else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
- else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
- else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
- else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
- else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
- if (DBG) log("getCdmaLevel=" + level);
- return level;
+ return mCdma.getLevel();
}
/**
@@ -794,29 +442,7 @@
*/
@UnsupportedAppUsage
public int getCdmaAsuLevel() {
- final int cdmaDbm = getCdmaDbm();
- final int cdmaEcio = getCdmaEcio();
- int cdmaAsuLevel;
- int ecioAsuLevel;
-
- if (cdmaDbm >= -75) cdmaAsuLevel = 16;
- else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
- else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
- else if (cdmaDbm >= -95) cdmaAsuLevel = 2;
- else if (cdmaDbm >= -100) cdmaAsuLevel = 1;
- else cdmaAsuLevel = 99;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) ecioAsuLevel = 16;
- else if (cdmaEcio >= -100) ecioAsuLevel = 8;
- else if (cdmaEcio >= -115) ecioAsuLevel = 4;
- else if (cdmaEcio >= -130) ecioAsuLevel = 2;
- else if (cdmaEcio >= -150) ecioAsuLevel = 1;
- else ecioAsuLevel = 99;
-
- int level = (cdmaAsuLevel < ecioAsuLevel) ? cdmaAsuLevel : ecioAsuLevel;
- if (DBG) log("getCdmaAsuLevel=" + level);
- return level;
+ return mCdma.getAsuLevel();
}
/**
@@ -826,26 +452,7 @@
*/
@UnsupportedAppUsage
public int getEvdoLevel() {
- int evdoDbm = getEvdoDbm();
- int evdoSnr = getEvdoSnr();
- int levelEvdoDbm;
- int levelEvdoSnr;
-
- if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
- else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
- else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
- else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
- else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
- else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
- else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
- else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
- else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- if (DBG) log("getEvdoLevel=" + level);
- return level;
+ return mCdma.getEvdoLevel();
}
/**
@@ -855,28 +462,7 @@
*/
@UnsupportedAppUsage
public int getEvdoAsuLevel() {
- int evdoDbm = getEvdoDbm();
- int evdoSnr = getEvdoSnr();
- int levelEvdoDbm;
- int levelEvdoSnr;
-
- if (evdoDbm >= -65) levelEvdoDbm = 16;
- else if (evdoDbm >= -75) levelEvdoDbm = 8;
- else if (evdoDbm >= -85) levelEvdoDbm = 4;
- else if (evdoDbm >= -95) levelEvdoDbm = 2;
- else if (evdoDbm >= -105) levelEvdoDbm = 1;
- else levelEvdoDbm = 99;
-
- if (evdoSnr >= 7) levelEvdoSnr = 16;
- else if (evdoSnr >= 6) levelEvdoSnr = 8;
- else if (evdoSnr >= 5) levelEvdoSnr = 4;
- else if (evdoSnr >= 3) levelEvdoSnr = 2;
- else if (evdoSnr >= 1) levelEvdoSnr = 1;
- else levelEvdoSnr = 99;
-
- int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- if (DBG) log("getEvdoAsuLevel=" + level);
- return level;
+ return mCdma.getEvdoAsuLevel();
}
/**
@@ -886,7 +472,7 @@
*/
@UnsupportedAppUsage
public int getLteDbm() {
- return mLteRsrp;
+ return mLte.getRsrp();
}
/**
@@ -896,83 +482,7 @@
*/
@UnsupportedAppUsage
public int getLteLevel() {
- /*
- * TS 36.214 Physical Layer Section 5.1.3
- * TS 36.331 RRC
- *
- * RSSI = received signal + noise
- * RSRP = reference signal dBm
- * RSRQ = quality of signal dB = Number of Resource blocks*RSRP/RSSI
- * SNR = gain = signal/noise ratio = -10log P1/P2 dB
- */
- int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
-
- if (mLteRsrp > MAX_LTE_RSRP || mLteRsrp < MIN_LTE_RSRP) {
- if (mLteRsrp != INVALID) {
- Log.wtf(LOG_TAG, "getLteLevel - invalid lte rsrp: mLteRsrp=" + mLteRsrp);
- }
- } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
- } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
- } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
- } else if (mLteRsrp >= (mLteRsrpThresholds[0] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_POOR;
- } else {
- rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- }
-
- if (useOnlyRsrpForLteLevel()) {
- log("getLTELevel - rsrp = " + rsrpIconLevel);
- if (rsrpIconLevel != -1) {
- return rsrpIconLevel;
- }
- }
-
- /*
- * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
- * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
- * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
- * Icon Only
- */
- if (mLteRssnr > 300) snrIconLevel = -1;
- else if (mLteRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
- else if (mLteRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
- else if (mLteRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
- else if (mLteRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
- else if (mLteRssnr >= -200)
- snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- if (DBG) log("getLTELevel - rsrp:" + mLteRsrp + " snr:" + mLteRssnr + " rsrpIconLevel:"
- + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
- + " lteRsrpBoost:" + mLteRsrpBoost);
-
- /* Choose a measurement type to use for notification */
- if (snrIconLevel != -1 && rsrpIconLevel != -1) {
- /*
- * The number of bars displayed shall be the smaller of the bars
- * associated with LTE RSRP and the bars associated with the LTE
- * RS_SNR
- */
- return (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
- }
-
- if (snrIconLevel != -1) return snrIconLevel;
-
- if (rsrpIconLevel != -1) return rsrpIconLevel;
-
- /* Valid values are (0-31, 99) as defined in TS 27.007 8.5 */
- if (mLteSignalStrength > 31) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (mLteSignalStrength >= 12) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
- else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
- else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
- else if (mLteSignalStrength >= 0) rssiIconLevel = SIGNAL_STRENGTH_POOR;
-
- if (DBG) log("getLteLevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
- + rssiIconLevel);
- return rssiIconLevel;
-
+ return mLte.getLevel();
}
/**
@@ -983,41 +493,14 @@
*/
@UnsupportedAppUsage
public int getLteAsuLevel() {
- int lteAsuLevel = 99;
- int lteDbm = getLteDbm();
- /*
- * 3GPP 27.007 (Ver 10.3.0) Sec 8.69
- * 0 -140 dBm or less
- * 1 -139 dBm
- * 2...96 -138... -44 dBm
- * 97 -43 dBm or greater
- * 255 not known or not detectable
- */
- /*
- * validateInput will always give a valid range between -140 t0 -44 as
- * per ril.h. so RSRP >= -43 & <-140 will fall under asu level 255
- * and not 97 or 0
- */
- if (lteDbm == SignalStrength.INVALID) lteAsuLevel = 255;
- else lteAsuLevel = lteDbm + 140;
- if (DBG) log("Lte Asu level: "+lteAsuLevel);
- return lteAsuLevel;
+ return mLte.getAsuLevel();
}
/**
* @return true if this is for GSM
*/
public boolean isGsm() {
- return this.mIsGsm;
- }
-
- /**
- * @return true if it uses only RSRP for the number of LTE signal bar, otherwise false.
- *
- * @hide
- */
- public boolean useOnlyRsrpForLteLevel() {
- return this.mUseOnlyRsrpForLteLevel;
+ return !(getPrimary() instanceof CellSignalStrengthCdma);
}
/**
@@ -1027,7 +510,7 @@
*/
@UnsupportedAppUsage
public int getTdScdmaDbm() {
- return this.mTdScdmaRscp;
+ return mTdscdma.getRscp();
}
/**
@@ -1040,19 +523,7 @@
*/
@UnsupportedAppUsage
public int getTdScdmaLevel() {
- final int tdScdmaDbm = getTdScdmaDbm();
- int level;
-
- if ((tdScdmaDbm > -25) || (tdScdmaDbm == SignalStrength.INVALID))
- level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT;
- else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD;
- else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE;
- else if (tdScdmaDbm >= -110) level = SIGNAL_STRENGTH_POOR;
- else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- if (DBG) log("getTdScdmaLevel = " + level);
- return level;
+ return mTdscdma.getLevel();
}
/**
@@ -1062,13 +533,7 @@
*/
@UnsupportedAppUsage
public int getTdScdmaAsuLevel() {
- final int tdScdmaDbm = getTdScdmaDbm();
- int tdScdmaAsuLevel;
-
- if (tdScdmaDbm == INVALID) tdScdmaAsuLevel = 255;
- else tdScdmaAsuLevel = tdScdmaDbm + 120;
- if (DBG) log("TD-SCDMA Asu level: " + tdScdmaAsuLevel);
- return tdScdmaAsuLevel;
+ return mTdscdma.getAsuLevel();
}
/**
@@ -1077,7 +542,7 @@
* @hide
*/
public int getWcdmaRscp() {
- return mWcdmaRscp;
+ return mWcdma.getRscp();
}
/**
@@ -1094,14 +559,7 @@
* 96 -24 dBm or greater
* 255 not known or not detectable
*/
- final int wcdmaDbm = getWcdmaDbm();
- int wcdmaAsuLevel = 255;
- // validateInput will always give a valid range between -120 to -24 as per ril.h. so RSCP
- // outside range is already set to INVALID
- if (wcdmaDbm == SignalStrength.INVALID) wcdmaAsuLevel = 255;
- else wcdmaAsuLevel = wcdmaDbm + 120;
- if (DBG) log("Wcdma Asu level: " + wcdmaAsuLevel);
- return wcdmaAsuLevel;
+ return mWcdma.getAsuLevel();
}
/**
@@ -1110,7 +568,7 @@
* @hide
*/
public int getWcdmaDbm() {
- return mWcdmaRscp;
+ return mWcdma.getDbm();
}
/**
@@ -1119,55 +577,7 @@
* @hide
*/
public int getWcdmaLevel() {
- int level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- if (mWcdmaDefaultSignalMeasurement == null) {
- Log.wtf(LOG_TAG, "getWcdmaLevel - WCDMA default signal measurement is invalid.");
- return level;
- }
-
- switch (mWcdmaDefaultSignalMeasurement) {
- case MEASUMENT_TYPE_RSCP:
- // RSCP valid values are (-120 through -24) as defined in TS 27.007 8.69
- if (mWcdmaRscp < MIN_WCDMA_RSCP || mWcdmaRscp > MAX_WCDMA_RSCP) {
- if (mWcdmaRscp != INVALID) {
- Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSCP: mWcdmaRscp="
- + mWcdmaRscp);
- }
- } else if (mWcdmaRscp >= mWcdmaRscpThresholds[3]) {
- level = SIGNAL_STRENGTH_GREAT;
- } else if (mWcdmaRscp >= mWcdmaRscpThresholds[2]) {
- level = SIGNAL_STRENGTH_GOOD;
- } else if (mWcdmaRscp >= mWcdmaRscpThresholds[1]) {
- level = SIGNAL_STRENGTH_MODERATE;
- } else if (mWcdmaRscp >= mWcdmaRscpThresholds[0]) {
- level = SIGNAL_STRENGTH_POOR;
- }
- if (DBG) log("getWcdmaLevel=" + level + " WcdmaRscp=" + mWcdmaRscp);
- break;
-
- default:
- // RSSI valid values are (0..31) as defined in TS 27.007 8.5
- if (mWcdmaSignalStrength < 0 || mWcdmaSignalStrength > 31) {
- if (mWcdmaSignalStrength != 99) {
- Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSSI: mWcdmaSignalStrength="
- + mWcdmaSignalStrength);
- }
- } else if (mWcdmaSignalStrength >= 18) {
- level = SIGNAL_STRENGTH_GREAT;
- } else if (mWcdmaSignalStrength >= 13) {
- level = SIGNAL_STRENGTH_GOOD;
- } else if (mWcdmaSignalStrength >= 8) {
- level = SIGNAL_STRENGTH_MODERATE;
- } else if (mWcdmaSignalStrength >= 3) {
- level = SIGNAL_STRENGTH_POOR;
- }
- if (DBG) log("getWcdmaLevel=" + level + " WcdmaSignalStrength=" +
- mWcdmaSignalStrength);
- break;
-
- }
- return level;
+ return mWcdma.getLevel();
}
/**
@@ -1175,18 +585,7 @@
*/
@Override
public int hashCode() {
- int primeNum = 31;
- return ((mGsmSignalStrength * primeNum)
- + (mGsmBitErrorRate * primeNum)
- + (mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
- + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
- + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
- + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
- + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum)
- + (mWcdmaSignalStrength * primeNum) + (mWcdmaRscpAsu * primeNum)
- + (mWcdmaRscp * primeNum) + (mIsGsm ? 1 : 0) + (mUseOnlyRsrpForLteLevel ? 1 : 0)
- + (Objects.hashCode(mWcdmaDefaultSignalMeasurement))
- + (Arrays.hashCode(mLteRsrpThresholds)) + (Arrays.hashCode(mWcdmaRscpThresholds)));
+ return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte, mLteRsrpBoost);
}
/**
@@ -1194,40 +593,16 @@
*/
@Override
public boolean equals (Object o) {
- SignalStrength s;
+ if (!(o instanceof SignalStrength)) return false;
- try {
- s = (SignalStrength) o;
- } catch (ClassCastException ex) {
- return false;
- }
+ SignalStrength s = (SignalStrength) o;
- if (o == null) {
- return false;
- }
-
- return (mGsmSignalStrength == s.mGsmSignalStrength
- && mGsmBitErrorRate == s.mGsmBitErrorRate
- && mCdmaDbm == s.mCdmaDbm
- && mCdmaEcio == s.mCdmaEcio
- && mEvdoDbm == s.mEvdoDbm
- && mEvdoEcio == s.mEvdoEcio
- && mEvdoSnr == s.mEvdoSnr
- && mLteSignalStrength == s.mLteSignalStrength
- && mLteRsrp == s.mLteRsrp
- && mLteRsrq == s.mLteRsrq
- && mLteRssnr == s.mLteRssnr
- && mLteCqi == s.mLteCqi
- && mLteRsrpBoost == s.mLteRsrpBoost
- && mTdScdmaRscp == s.mTdScdmaRscp
- && mWcdmaSignalStrength == s.mWcdmaSignalStrength
- && mWcdmaRscpAsu == s.mWcdmaRscpAsu
- && mWcdmaRscp == s.mWcdmaRscp
- && mIsGsm == s.mIsGsm
- && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
- && Objects.equals(mWcdmaDefaultSignalMeasurement, s.mWcdmaDefaultSignalMeasurement)
- && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds)
- && Arrays.equals(mWcdmaRscpThresholds, s.mWcdmaRscpThresholds));
+ return mCdma.equals(s.mCdma)
+ && mGsm.equals(s.mGsm)
+ && mWcdma.equals(s.mWcdma)
+ && mTdscdma.equals(s.mTdscdma)
+ && mLte.equals(s.mLte)
+ && mLteRsrpBoost == s.mLteRsrpBoost;
}
/**
@@ -1235,63 +610,16 @@
*/
@Override
public String toString() {
- return ("SignalStrength:"
- + " " + mGsmSignalStrength
- + " " + mGsmBitErrorRate
- + " " + mCdmaDbm
- + " " + mCdmaEcio
- + " " + mEvdoDbm
- + " " + mEvdoEcio
- + " " + mEvdoSnr
- + " " + mLteSignalStrength
- + " " + mLteRsrp
- + " " + mLteRsrq
- + " " + mLteRssnr
- + " " + mLteCqi
- + " " + mLteRsrpBoost
- + " " + mTdScdmaRscp
- + " " + mWcdmaSignalStrength
- + " " + mWcdmaRscpAsu
- + " " + mWcdmaRscp
- + " " + (mIsGsm ? "gsm|lte" : "cdma")
- + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
- "use_rsrp_and_rssnr_for_lte_level")
- + " " + mWcdmaDefaultSignalMeasurement
- + " " + (Arrays.toString(mLteRsrpThresholds))
- + " " + (Arrays.toString(mWcdmaRscpThresholds)));
- }
-
- /** Returns the signal strength related to GSM. */
- private int getGsmRelatedSignalStrength() {
- int level = getLteLevel();
- if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- level = getTdScdmaLevel();
- if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- level = getWcdmaLevel();
- if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- level = getGsmLevel();
- }
- }
- }
- return level;
- }
-
- /** Returns the signal strength related to CDMA. */
- private int getCdmaRelatedSignalStrength() {
- int level;
- int cdmaLevel = getCdmaLevel();
- int evdoLevel = getEvdoLevel();
- if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- /* We don't know evdo, use cdma */
- level = cdmaLevel;
- } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- /* We don't know cdma, use evdo */
- level = evdoLevel;
- } else {
- /* We know both, use the lowest level */
- level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
- }
- return level;
+ return new StringBuilder().append("SignalStrength:{")
+ .append("mCdma=").append(mCdma)
+ .append(",mGsm=").append(mGsm)
+ .append(",mWcdma=").append(mWcdma)
+ .append(",mTdscdma=").append(mTdscdma)
+ .append(",mLte=").append(mLte)
+ .append(",mLteRsrpBoost=").append(mLteRsrpBoost)
+ .append(",primary=").append(getPrimary().getClass().getSimpleName())
+ .append("}")
+ .toString();
}
/**
@@ -1302,34 +630,13 @@
*/
@UnsupportedAppUsage
private void setFromNotifierBundle(Bundle m) {
- mGsmSignalStrength = m.getInt("GsmSignalStrength");
- mGsmBitErrorRate = m.getInt("GsmBitErrorRate");
- mCdmaDbm = m.getInt("CdmaDbm");
- mCdmaEcio = m.getInt("CdmaEcio");
- mEvdoDbm = m.getInt("EvdoDbm");
- mEvdoEcio = m.getInt("EvdoEcio");
- mEvdoSnr = m.getInt("EvdoSnr");
- mLteSignalStrength = m.getInt("LteSignalStrength");
- mLteRsrp = m.getInt("LteRsrp");
- mLteRsrq = m.getInt("LteRsrq");
- mLteRssnr = m.getInt("LteRssnr");
- mLteCqi = m.getInt("LteCqi");
+ mCdma = m.getParcelable("Cdma");
+ mGsm = m.getParcelable("Gsm");
+ mWcdma = m.getParcelable("Wcdma");
+ mTdscdma = m.getParcelable("Tdscdma");
+ mLte = m.getParcelable("Lte");
+
mLteRsrpBoost = m.getInt("LteRsrpBoost");
- mTdScdmaRscp = m.getInt("TdScdma");
- mWcdmaSignalStrength = m.getInt("WcdmaSignalStrength");
- mWcdmaRscpAsu = m.getInt("WcdmaRscpAsu");
- mWcdmaRscp = m.getInt("WcdmaRscp");
- mIsGsm = m.getBoolean("IsGsm");
- mUseOnlyRsrpForLteLevel = m.getBoolean("UseOnlyRsrpForLteLevel");
- mWcdmaDefaultSignalMeasurement = m.getString("WcdmaDefaultSignalMeasurement");
- ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
- for (int i = 0; i < lteRsrpThresholds.size(); i++) {
- mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
- }
- ArrayList<Integer> wcdmaRscpThresholds = m.getIntegerArrayList("wcdmaRscpThresholds");
- for (int i = 0; i < wcdmaRscpThresholds.size(); i++) {
- mWcdmaRscpThresholds[i] = wcdmaRscpThresholds.get(i);
- }
}
/**
@@ -1340,56 +647,13 @@
*/
@UnsupportedAppUsage
public void fillInNotifierBundle(Bundle m) {
- m.putInt("GsmSignalStrength", mGsmSignalStrength);
- m.putInt("GsmBitErrorRate", mGsmBitErrorRate);
- m.putInt("CdmaDbm", mCdmaDbm);
- m.putInt("CdmaEcio", mCdmaEcio);
- m.putInt("EvdoDbm", mEvdoDbm);
- m.putInt("EvdoEcio", mEvdoEcio);
- m.putInt("EvdoSnr", mEvdoSnr);
- m.putInt("LteSignalStrength", mLteSignalStrength);
- m.putInt("LteRsrp", mLteRsrp);
- m.putInt("LteRsrq", mLteRsrq);
- m.putInt("LteRssnr", mLteRssnr);
- m.putInt("LteCqi", mLteCqi);
+ m.putParcelable("Cdma", mCdma);
+ m.putParcelable("Gsm", mGsm);
+ m.putParcelable("Wcdma", mWcdma);
+ m.putParcelable("Tdscdma", mTdscdma);
+ m.putParcelable("Lte", mLte);
+
m.putInt("LteRsrpBoost", mLteRsrpBoost);
- m.putInt("TdScdma", mTdScdmaRscp);
- m.putInt("WcdmaSignalStrength", mWcdmaSignalStrength);
- m.putInt("WcdmaRscpAsu", mWcdmaRscpAsu);
- m.putInt("WcdmaRscp", mWcdmaRscp);
- m.putBoolean("IsGsm", mIsGsm);
- m.putBoolean("UseOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
- m.putString("WcdmaDefaultSignalMeasurement", mWcdmaDefaultSignalMeasurement);
- ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
- for (int value : mLteRsrpThresholds) {
- lteRsrpThresholds.add(value);
- }
- m.putIntegerArrayList("lteRsrpThresholds", lteRsrpThresholds);
- ArrayList<Integer> wcdmaRscpThresholds = new ArrayList<Integer>();
- for (int value : mWcdmaRscpThresholds) {
- wcdmaRscpThresholds.add(value);
- }
- m.putIntegerArrayList("wcdmaRscpThresholds", wcdmaRscpThresholds);
- }
-
- /**
- * Gets the default threshold array for determining the display level of LTE signal bar.
- *
- * @return int array for determining the display level.
- */
- private int[] getDefaultLteRsrpThresholds() {
- return CarrierConfigManager.getDefaultConfig().getIntArray(
- CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
- }
-
- /**
- * Gets the default threshold array for determining the display level of WCDMA signal bar.
- *
- * @return int array for determining the display level.
- */
- private int[] getDefaultWcdmaRscpThresholds() {
- return CarrierConfigManager.getDefaultConfig().getIntArray(
- CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b61e99b..2c712a1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2617,8 +2617,7 @@
if (availableList == null) {
return null;
} else {
- return getAvailableSubscriptionInfoList().stream()
- .filter(subInfo -> !shouldHideSubscription(subInfo))
+ return availableList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
.collect(Collectors.toList());
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c7df36b..3b2a05b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4908,7 +4908,7 @@
*/
@RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
public void requestCellInfoUpdate(
- @NonNull Executor executor, @NonNull CellInfoCallback callback) {
+ @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
ITelephony telephony = getITelephony();
if (telephony == null) return;
@@ -6194,196 +6194,258 @@
NETWORK_MODE_LTE_TDSCDMA_WCDMA,
NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA,
NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
- NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+ NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+ NETWORK_MODE_NR_ONLY,
+ NETWORK_MODE_NR_LTE,
+ NETWORK_MODE_NR_LTE_CDMA_EVDO,
+ NETWORK_MODE_NR_LTE_GSM_WCDMA,
+ NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA,
+ NETWORK_MODE_NR_LTE_WCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA_GSM,
+ NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
})
@Retention(RetentionPolicy.SOURCE)
public @interface PrefNetworkMode{}
/**
- * preferred network mode is GSM/WCDMA (WCDMA preferred).
+ * Preferred network mode is GSM/WCDMA (WCDMA preferred).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
/**
- * preferred network mode is GSM only.
+ * Preferred network mode is GSM only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
/**
- * preferred network mode is WCDMA only.
+ * Preferred network mode is WCDMA only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
/**
- * preferred network mode is GSM/WCDMA (auto mode, according to PRL).
+ * Preferred network mode is GSM/WCDMA (auto mode, according to PRL).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
/**
- * preferred network mode is CDMA and EvDo (auto mode, according to PRL).
+ * Preferred network mode is CDMA and EvDo (auto mode, according to PRL).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
/**
- * preferred network mode is CDMA only.
+ * Preferred network mode is CDMA only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
/**
- * preferred network mode is EvDo only.
+ * Preferred network mode is EvDo only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
/**
- * preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
+ * Preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
/**
- * preferred network mode is LTE, CDMA and EvDo.
+ * Preferred network mode is LTE, CDMA and EvDo.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
/**
- * preferred network mode is LTE, GSM/WCDMA.
+ * Preferred network mode is LTE, GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
/**
- * preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+ * Preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
/**
- * preferred network mode is LTE Only.
+ * Preferred network mode is LTE Only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
/**
- * preferred network mode is LTE/WCDMA.
+ * Preferred network mode is LTE/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA;
/**
- * preferred network mode is TD-SCDMA only.
+ * Preferred network mode is TD-SCDMA only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
/**
- * preferred network mode is TD-SCDMA and WCDMA.
+ * Preferred network mode is TD-SCDMA and WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
/**
- * preferred network mode is TD-SCDMA and LTE.
+ * Preferred network mode is TD-SCDMA and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
/**
- * preferred network mode is TD-SCDMA and GSM.
+ * Preferred network mode is TD-SCDMA and GSM.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
/**
- * preferred network mode is TD-SCDMA,GSM and LTE.
+ * Preferred network mode is TD-SCDMA,GSM and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_GSM =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
/**
- * preferred network mode is TD-SCDMA, GSM/WCDMA.
+ * Preferred network mode is TD-SCDMA, GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA =
RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
/**
- * preferred network mode is TD-SCDMA, WCDMA and LTE.
+ * Preferred network mode is TD-SCDMA, WCDMA and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
/**
- * preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
+ * Preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
/**
- * preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+ * Preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
/**
- * preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+ * Preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
/**
+ * Preferred network mode is NR 5G only.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_ONLY = RILConstants.NETWORK_MODE_NR_ONLY;
+
+ /**
+ * Preferred network mode is NR 5G, LTE.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE = RILConstants.NETWORK_MODE_NR_LTE;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, CDMA and EvDo.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO =
+ RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, CDMA, EvDo, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_WCDMA = RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE and TDSCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA = RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA and GSM.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA, WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
+ /**
* Get the preferred network type.
* Used for device configuration by some CDMA operators.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return the preferred network type.
* @hide
*/
- @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
- @SystemApi
+ @RequiresPermission((android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE))
+ @UnsupportedAppUsage
public @PrefNetworkMode int getPreferredNetworkType(int subId) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
+ if (telephony != null) {
return telephony.getPreferredNetworkType(subId);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
@@ -6393,6 +6455,37 @@
}
/**
+ * Get the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @return a 32-bit bitmap.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public @NetworkTypeBitMask int getPreferredNetworkTypeBitmap() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return RadioAccessFamily.getRafFromNetworkType(
+ telephony.getPreferredNetworkType(getSubId()));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex);
+ }
+ return 0;
+ }
+
+ /**
* Sets the network selection mode to automatic.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -6607,6 +6700,37 @@
}
/**
+ * Set the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param networkTypeBitmap a 32-bit bitmap.
+ * @return true on success; false on any failure.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask int networkTypeBitmap) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setPreferredNetworkType(
+ getSubId(), RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmap));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType NPE", ex);
+ }
+ return false;
+ }
+
+ /**
* Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ebf1987..6326cc6 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -61,7 +61,6 @@
public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
"android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
-
/**
* Broadcast Action: The eUICC OTA status is changed.
* <p class="note">
@@ -87,6 +86,20 @@
"android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
/**
+ * Intent action to select a profile to enable before download a new eSIM profile.
+ *
+ * May be called during device provisioning when there are multiple slots having profiles on
+ * them. This Intent launches a screen for all the current existing profiles and let users to
+ * choose which one they want to enable. In this case, the slot contains the profile will be
+ * activated.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_PROFILE_SELECTION =
+ "android.telephony.euicc.action.PROFILE_SELECTION";
+
+ /**
* Intent action to provision an embedded subscription.
*
* <p>May be called during device provisioning to launch a screen to perform embedded SIM
@@ -132,6 +145,16 @@
public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2;
/**
+ * Key for an extra set on the {@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} intent for which
+ * kind of activation flow will be evolved. (see {@code EUICC_ACTIVATION_})
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_ACTIVATION_TYPE =
+ "android.telephony.euicc.extra.ACTIVATION_TYPE";
+
+ /**
* Key for an extra set on {@link PendingIntent} result callbacks providing a detailed result
* code.
*
@@ -197,6 +220,52 @@
public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
/**
+ * Euicc activation type which will be included in {@link #EXTRA_ACTIVATION_TYPE} and used to
+ * decide which kind of activation flow should be lauched.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"EUICC_ACTIVATION_"}, value = {
+ EUICC_ACTIVATION_TYPE_DEFAULT,
+ EUICC_ACTIVATION_TYPE_BACKUP,
+ EUICC_ACTIVATION_TYPE_TRANSFER
+
+ })
+ public @interface EuiccActivationType{}
+
+
+ /**
+ * The default euicc activation type which includes checking server side and downloading the
+ * profile based on carrier's download configuration.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1;
+
+ /**
+ * The euicc activation type used when the default download process failed. LPA will start the
+ * backup flow and try to download the profile for the carrier.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2;
+
+ /**
+ * The activation flow of eSIM seamless transfer will be used. LPA will start normal eSIM
+ * activation flow and if it's failed, the name of the carrier selected will be recorded. After
+ * the future device pairing, LPA will contact this carrier to transfer it from the other device
+ * to this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3;
+
+
+ /**
* Euicc OTA update status which can be got by {@link #getOtaStatus}
* @hide
*/
@@ -336,7 +405,7 @@
}
try {
getIEuiccController().downloadSubscription(subscription, switchAfterDownload,
- mContext.getOpPackageName(), callbackIntent);
+ mContext.getOpPackageName(), null /* resolvedBundle */, callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 2ebe870..f2cb653 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -128,32 +128,108 @@
int OEM_ERROR_25 = 525;
/* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
- int NETWORK_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */
- int NETWORK_MODE_GSM_ONLY = 1; /* GSM only */
- int NETWORK_MODE_WCDMA_ONLY = 2; /* WCDMA only */
- int NETWORK_MODE_GSM_UMTS = 3; /* GSM/WCDMA (auto mode, according to PRL)
- AVAILABLE Application Settings menu*/
- int NETWORK_MODE_CDMA = 4; /* CDMA and EvDo (auto mode, according to PRL)
- AVAILABLE Application Settings menu*/
- int NETWORK_MODE_CDMA_NO_EVDO = 5; /* CDMA only */
- int NETWORK_MODE_EVDO_NO_CDMA = 6; /* EvDo only */
- int NETWORK_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL)
- AVAILABLE Application Settings menu*/
- int NETWORK_MODE_LTE_CDMA_EVDO = 8; /* LTE, CDMA and EvDo */
- int NETWORK_MODE_LTE_GSM_WCDMA = 9; /* LTE, GSM/WCDMA */
- int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */
- int NETWORK_MODE_LTE_ONLY = 11; /* LTE Only mode. */
- int NETWORK_MODE_LTE_WCDMA = 12; /* LTE/WCDMA */
- int NETWORK_MODE_TDSCDMA_ONLY = 13; /* TD-SCDMA only */
- int NETWORK_MODE_TDSCDMA_WCDMA = 14; /* TD-SCDMA and WCDMA */
- int NETWORK_MODE_LTE_TDSCDMA = 15; /* TD-SCDMA and LTE */
- int NETWORK_MODE_TDSCDMA_GSM = 16; /* TD-SCDMA and GSM */
- int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; /* TD-SCDMA,GSM and LTE */
- int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; /* TD-SCDMA, GSM/WCDMA */
- int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; /* TD-SCDMA, WCDMA and LTE */
- int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; /* TD-SCDMA, GSM/WCDMA and LTE */
- int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; /*TD-SCDMA,EvDo,CDMA,GSM/WCDMA*/
- int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; /* TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo */
+ /** GSM, WCDMA (WCDMA preferred) */
+ int NETWORK_MODE_WCDMA_PREF = 0;
+
+ /** GSM only */
+ int NETWORK_MODE_GSM_ONLY = 1;
+
+ /** WCDMA only */
+ int NETWORK_MODE_WCDMA_ONLY = 2;
+
+ /** GSM, WCDMA (auto mode, according to PRL) */
+ int NETWORK_MODE_GSM_UMTS = 3;
+
+ /** CDMA and EvDo (auto mode, according to PRL) */
+ int NETWORK_MODE_CDMA = 4;
+
+ /** CDMA only */
+ int NETWORK_MODE_CDMA_NO_EVDO = 5;
+
+ /** EvDo only */
+ int NETWORK_MODE_EVDO_NO_CDMA = 6;
+
+ /** GSM, WCDMA, CDMA, and EvDo (auto mode, according to PRL) */
+ int NETWORK_MODE_GLOBAL = 7;
+
+ /** LTE, CDMA and EvDo */
+ int NETWORK_MODE_LTE_CDMA_EVDO = 8;
+
+ /** LTE, GSM and WCDMA */
+ int NETWORK_MODE_LTE_GSM_WCDMA = 9;
+
+ /** LTE, CDMA, EvDo, GSM, and WCDMA */
+ int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10;
+
+ /** LTE only mode. */
+ int NETWORK_MODE_LTE_ONLY = 11;
+
+ /** LTE and WCDMA */
+ int NETWORK_MODE_LTE_WCDMA = 12;
+
+ /** TD-SCDMA only */
+ int NETWORK_MODE_TDSCDMA_ONLY = 13;
+
+ /** TD-SCDMA and WCDMA */
+ int NETWORK_MODE_TDSCDMA_WCDMA = 14;
+
+ /** LTE and TD-SCDMA*/
+ int NETWORK_MODE_LTE_TDSCDMA = 15;
+
+ /** TD-SCDMA and GSM */
+ int NETWORK_MODE_TDSCDMA_GSM = 16;
+
+ /** TD-SCDMA, GSM and LTE */
+ int NETWORK_MODE_LTE_TDSCDMA_GSM = 17;
+
+ /** TD-SCDMA, GSM and WCDMA */
+ int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18;
+
+ /** LTE, TD-SCDMA and WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19;
+
+ /** LTE, TD-SCDMA, GSM, and WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20;
+
+ /** TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
+ int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21;
+
+ /** LTE, TDCSDMA, CDMA, EVDO, GSM and WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22;
+
+ /** NR 5G only mode */
+ int NETWORK_MODE_NR_ONLY = 23;
+
+ /** NR 5G, LTE */
+ int NETWORK_MODE_NR_LTE = 24;
+
+ /** NR 5G, LTE, CDMA and EvDo */
+ int NETWORK_MODE_NR_LTE_CDMA_EVDO = 25;
+
+ /** NR 5G, LTE, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_GSM_WCDMA = 26;
+
+ /** NR 5G, LTE, CDMA, EvDo, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = 27;
+
+ /** NR 5G, LTE and WCDMA */
+ int NETWORK_MODE_NR_LTE_WCDMA = 28;
+
+ /** NR 5G, LTE and TDSCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA = 29;
+
+ /** NR 5G, LTE, TD-SCDMA and GSM */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_GSM = 30;
+
+ /** NR 5G, LTE, TD-SCDMA, WCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA = 31;
+
+ /** NR 5G, LTE, TD-SCDMA, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = 32;
+
+ /** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33;
+
int PREFERRED_NETWORK_MODE = Integer.parseInt(TelephonyManager.getTelephonyProperty(0,
"ro.telephony.default_network", Integer.toString(NETWORK_MODE_WCDMA_PREF)));
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 553e3fb..0edc002 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -284,8 +284,6 @@
*/
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
- Log.wtf(LOG_TAG,
- "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
// If the device identifier check is enabled then enforce the new access requirements for
// both 1P and 3P apps.
boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
@@ -295,17 +293,40 @@
boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED, 0) == 1;
boolean is3PApp = true;
+ // Also check if the application is a preloaded non-privileged app; if so there is a
+ // separate setting to relax the check for these apps to ensure users can relax the check
+ // for 3P or non-priv apps as needed while continuing to test the other.
+ boolean relaxNonPrivDeviceIdentifierCheck = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED, 0) == 1;
+ boolean isNonPrivApp = false;
ApplicationInfo callingPackageInfo = null;
try {
callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
- if (callingPackageInfo.isSystemApp()) {
+ if (callingPackageInfo.isPrivilegedApp()) {
is3PApp = false;
+ } else if (callingPackageInfo.isSystemApp()) {
+ is3PApp = false;
+ isNonPrivApp = true;
}
} catch (PackageManager.NameNotFoundException e) {
// If the application info for the calling package could not be found then assume the
// calling app is a 3P app to detect any issues with the check
+ Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
+ e);
}
- if (enableDeviceIdentifierCheck || (is3PApp && !relax3PDeviceIdentifierCheck)) {
+ Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+ + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
+ // The new Q restrictions for device identifier access will be enforced if any of the
+ // following are true:
+ // - The PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED setting has been set.
+ // - The app requesting a device identifier is not a preloaded app (3P), and the
+ // PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED setting has not been set.
+ // - The app requesting a device identifier is a preloaded app but is not a privileged app,
+ // and the PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED setting has not been set.
+ if (enableDeviceIdentifierCheck
+ || (is3PApp && !relax3PDeviceIdentifierCheck)
+ || (isNonPrivApp && !relaxNonPrivDeviceIdentifierCheck)) {
boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
if (callingPackage != null) {
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 0a0ad90..870a689 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -32,7 +32,7 @@
String getEid();
int getOtaStatus();
oneway void downloadSubscription(in DownloadableSubscription subscription,
- boolean switchAfterDownload, String callingPackage, in PendingIntent callbackIntent);
+ boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle, in PendingIntent callbackIntent);
EuiccInfo getEuiccInfo();
oneway void deleteSubscription(int subscriptionId, String callingPackage,
in PendingIntent callbackIntent);
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 369a002..4c2a984 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -16,16 +16,16 @@
package com.android.framework.permission.tests;
-import android.content.res.Configuration;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.IWindowManager;
-import junit.framework.TestCase;
-import static android.view.Display.DEFAULT_DISPLAY;
+import junit.framework.TestCase;
/**
* TODO: Remove this. This is only a placeholder, need to implement this.
@@ -73,17 +73,6 @@
}
try {
- mWm.updateOrientationFromAppTokens(new Configuration(),
- null /* freezeThisOneIfNeeded */, DEFAULT_DISPLAY);
- fail("IWindowManager.updateOrientationFromAppTokens did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
+ " expected");
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
index 5fa065a..d18c126 100644
--- a/tests/testables/src/android/testing/BaseFragmentTest.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -21,7 +21,9 @@
import android.app.FragmentController;
import android.app.FragmentHostCallback;
import android.app.FragmentManagerNonConfig;
+import android.content.Context;
import android.graphics.PixelFormat;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.support.test.InstrumentationRegistry;
@@ -75,7 +77,7 @@
TestableLooper.get(this).runWithLooper(() -> {
mHandler = new Handler();
- mFragment = mCls.newInstance();
+ mFragment = instantiate(mContext, mCls.getName(), null);
mFragments = FragmentController.createController(new HostCallbacks());
mFragments.attachHost(null);
mFragments.getFragmentManager().beginTransaction()
@@ -187,6 +189,13 @@
TestableLooper.get(this).processAllMessages();
}
+ /**
+ * Method available for override to replace fragment instantiation.
+ */
+ protected Fragment instantiate(Context context, String className, @Nullable Bundle arguments) {
+ return Fragment.instantiate(context, className, arguments);
+ }
+
private View findViewById(int id) {
return mView.findViewById(id);
}
@@ -206,6 +215,11 @@
}
@Override
+ public Fragment instantiate(Context context, String className, Bundle arguments) {
+ return BaseFragmentTest.this.instantiate(context, className, arguments);
+ }
+
+ @Override
public boolean onShouldSaveFragmentState(Fragment fragment) {
return true; // True for now.
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c91134c..58702dc 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@
ResourceId id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
bool allow_new = false;
- Maybe<Overlayable> overlayable;
+ Maybe<OverlayableItem> overlayable_item;
std::string comment;
std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@
}
}
- if (res->overlayable) {
- if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
+ if (res->overlayable_item) {
+ if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) {
return false;
}
}
@@ -1059,92 +1059,119 @@
bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (out_resource->config != ConfigDescription::DefaultConfig()) {
diag_->Warn(DiagMessage(out_resource->source)
- << "ignoring configuration '" << out_resource->config
- << "' for <overlayable> tag");
+ << "ignoring configuration '" << out_resource->config
+ << "' for <overlayable> tag");
}
+ Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!overlayable_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<overlayable> tag must have a 'name' attribute");
+ return false;
+ }
+
+ const std::string kActorUriScheme =
+ android::base::StringPrintf("%s://", Overlayable::kActorScheme);
+ Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+ if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "specified <overlayable> tag 'actor' attribute must use the scheme '"
+ << Overlayable::kActorScheme << "'");
+ return false;
+ }
+
+ // Create a overlayable entry grouping that represents this <overlayable>
+ auto overlayable = std::make_shared<Overlayable>(
+ overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
+ out_resource->source);
+
bool error = false;
std::string comment;
- Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
+ OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
const size_t start_depth = parser->depth();
while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
xml::XmlPullParser::Event event = parser->event();
if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
- // Break the loop when exiting the overlayable element
+ // Break the loop when exiting the <overlayable>
break;
} else if (event == xml::XmlPullParser::Event::kEndElement
&& parser->depth() == start_depth + 1) {
- // Clear the current policies when exiting the policy element
- current_policies = Overlayable::Policy::kNone;
+ // Clear the current policies when exiting the <policy> tags
+ current_policies = OverlayableItem::Policy::kNone;
continue;
} else if (event == xml::XmlPullParser::Event::kComment) {
- // Get the comment of individual item elements
+ // Retrieve the comment of individual <item> tags
comment = parser->comment();
continue;
} else if (event != xml::XmlPullParser::Event::kStartElement) {
- // Skip to the next element
+ // Skip to the start of the next element
continue;
}
- const Source item_source = source_.WithLine(parser->line_number());
+ const Source element_source = source_.WithLine(parser->line_number());
const std::string& element_name = parser->element_name();
const std::string& element_namespace = parser->element_namespace();
if (element_namespace.empty() && element_name == "item") {
// Items specify the name and type of resource that should be overlayable
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
- if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'name' attribute");
+ Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!item_name) {
+ diag_->Error(DiagMessage(element_source)
+ << "<item> within an <overlayable> must have a 'name' attribute");
error = true;
continue;
}
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
- if (!maybe_type) {
- diag_->Error(DiagMessage(item_source)
- << "<item> within an <overlayable> tag must have a 'type' attribute");
+ Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!item_type) {
+ diag_->Error(DiagMessage(element_source)
+ << "<item> within an <overlayable> must have a 'type' attribute");
error = true;
continue;
}
- const ResourceType* type = ParseResourceType(maybe_type.value());
+ const ResourceType* type = ParseResourceType(item_type.value());
if (type == nullptr) {
- diag_->Error(DiagMessage(item_source)
- << "invalid resource type '" << maybe_type.value()
+ diag_->Error(DiagMessage(element_source)
+ << "invalid resource type '" << item_type.value()
<< "' in <item> within an <overlayable>");
error = true;
continue;
}
- ParsedResource child_resource;
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies = current_policies;
+ overlayable_item.comment = comment;
+ overlayable_item.source = element_source;
+
+ ParsedResource child_resource{};
child_resource.name.type = *type;
- child_resource.name.entry = maybe_name.value().to_string();
- child_resource.overlayable = Overlayable{current_policies, item_source, comment};
+ child_resource.name.entry = item_name.value().to_string();
+ child_resource.overlayable_item = overlayable_item;
out_resource->child_resources.push_back(std::move(child_resource));
} else if (element_namespace.empty() && element_name == "policy") {
- if (current_policies != Overlayable::Policy::kNone) {
+ if (current_policies != OverlayableItem::Policy::kNone) {
// If the policy list is not empty, then we are currently inside a policy element
- diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
+ diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
error = true;
break;
} else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
- // policies
+ // policies. Items within the policy tag will have the specified policy.
for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
if (trimmed_part == "public") {
- current_policies |= Overlayable::Policy::kPublic;
+ current_policies |= OverlayableItem::Policy::kPublic;
} else if (trimmed_part == "product") {
- current_policies |= Overlayable::Policy::kProduct;
+ current_policies |= OverlayableItem::Policy::kProduct;
} else if (trimmed_part == "product_services") {
- current_policies |= Overlayable::Policy::kProductServices;
+ current_policies |= OverlayableItem::Policy::kProductServices;
} else if (trimmed_part == "system") {
- current_policies |= Overlayable::Policy::kSystem;
+ current_policies |= OverlayableItem::Policy::kSystem;
} else if (trimmed_part == "vendor") {
- current_policies |= Overlayable::Policy::kVendor;
+ current_policies |= OverlayableItem::Policy::kVendor;
} else {
- diag_->Error(DiagMessage(item_source)
+ diag_->Error(DiagMessage(element_source)
<< "<policy> has unsupported type '" << trimmed_part << "'");
error = true;
continue;
@@ -1152,11 +1179,13 @@
}
}
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
- diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
+ diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
<< " in <overlayable>");
error = true;
break;
}
+
+ comment.clear();
}
return !error;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 03e6197..debca9c 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -892,11 +892,8 @@
}
TEST_F(ResourceParserTest, ParseOverlayable) {
- std::string input = R"(<overlayable />)";
- EXPECT_TRUE(TestParse(input));
-
- input = R"(
- <overlayable>
+ std::string input = R"(
+ <overlayable name="Name" actor="overlay://theme">
<item type="string" name="foo" />
<item type="drawable" name="bar" />
</overlayable>)";
@@ -905,24 +902,35 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
- Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
- Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
+ EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)"));
+ EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)"));
+ EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)"));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) {
+ EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)"));
}
TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
- std::string input = R"(<overlayable />)";
- EXPECT_TRUE(TestParse(input));
-
- input = R"(
- <overlayable>
+ std::string input = R"(
+ <overlayable name="Name">
<item type="string" name="foo" />
<policy type="product">
<item type="string" name="bar" />
@@ -945,49 +953,55 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
}
TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="illegal_policy">
<item type="string" name="foo" />
</policy>
@@ -995,7 +1009,7 @@
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item name="foo" />
</policy>
@@ -1003,7 +1017,7 @@
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="vendor">
<item type="string" />
</policy>
@@ -1013,7 +1027,7 @@
TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="vendor|product_services">
<item type="string" name="foo" />
</policy>
@@ -1026,39 +1040,59 @@
auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
- | Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
+ | OverlayableItem::Policy::kProductServices));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
- | Overlayable::Policy::kSystem));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kSystem));
}
TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
<item type="string" name="foo" />
<item type="string" name="foo" />
</overlayable>)";
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<item type="string" name="foo" />
</overlayable>
- <overlayable>
+ <overlayable name="Name">
<item type="string" name="foo" />
</overlayable>)";
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
+ <item type="string" name="foo" />
+ </overlayable>
+ <overlayable name="Other">
+ <item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable name="Name" actor="overlay://my.actor.one">
+ <item type="string" name="foo" />
+ </overlayable>
+ <overlayable name="Other" actor="overlay://my.actor.two">
+ <item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
<item type="string" name="foo" />
@@ -1067,7 +1101,7 @@
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
@@ -1076,7 +1110,7 @@
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
@@ -1087,13 +1121,13 @@
EXPECT_FALSE(TestParse(input));
input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
</overlayable>
- <overlayable>
+ <overlayable name="Name">
<policy type="product">
<item type="string" name="foo" />
</policy>
@@ -1103,7 +1137,7 @@
TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
std::string input = R"(
- <overlayable>
+ <overlayable name="Name">
<policy type="vendor|product">
<policy type="product_services">
<item type="string" name="foo" />
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 54633ad..dbd0a0c 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -40,6 +40,8 @@
namespace aapt {
+const char* Overlayable::kActorScheme = "overlay";
+
static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
@@ -625,17 +627,18 @@
return true;
}
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
IDiagnostics* diag) {
return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
}
bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
- const Overlayable& overlayable, IDiagnostics* diag) {
+ const OverlayableItem& overlayable, IDiagnostics* diag) {
return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
}
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
+ const OverlayableItem& overlayable,
NameValidator name_validator, IDiagnostics *diag) {
CHECK(diag != nullptr);
@@ -647,14 +650,15 @@
ResourceTableType* type = package->FindOrCreateType(name.type);
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
- if (entry->overlayable) {
+ if (entry->overlayable_item) {
diag->Error(DiagMessage(overlayable.source)
- << "duplicate overlayable declaration for resource '" << name << "'");
- diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+ << "duplicate overlayable declaration for resource '" << name << "'");
+ diag->Error(DiagMessage(entry->overlayable_item.value().source)
+ << "previous declaration here");
return false;
}
- entry->overlayable = overlayable;
+ entry->overlayable_item = overlayable;
return true;
}
@@ -690,7 +694,7 @@
new_entry->id = entry->id;
new_entry->visibility = entry->visibility;
new_entry->allow_new = entry->allow_new;
- new_entry->overlayable = entry->overlayable;
+ new_entry->overlayable_item = entry->overlayable_item;
for (const auto& config_value : entry->values) {
ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index e646f5b..eaf6a47 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,10 +57,27 @@
std::string comment;
};
-// Represents a declaration that a resource is overlayable at runtime.
struct Overlayable {
+ Overlayable() = default;
+ Overlayable(const android::StringPiece& name, const android::StringPiece& actor)
+ : name(name.to_string()), actor(actor.to_string()) {}
+ Overlayable(const android::StringPiece& name, const android::StringPiece& actor,
+ const Source& source)
+ : name(name.to_string()), actor(actor.to_string()), source(source ){}
+
+ static const char* kActorScheme;
+ std::string name;
+ std::string actor;
+ Source source;
+};
+
+// Represents a declaration that a resource is overlayable at runtime.
+struct OverlayableItem {
+ explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
+ : overlayable(overlayable) {}
// Represents the types overlays that are allowed to overlay the resource.
+ typedef uint32_t PolicyFlags;
enum Policy : uint32_t {
kNone = 0x00,
@@ -80,11 +97,10 @@
kProductServices = 0x10
};
- typedef uint32_t PolicyFlags;
+ std::shared_ptr<Overlayable> overlayable;
PolicyFlags policies = Policy::kNone;
-
- Source source;
std::string comment;
+ Source source;
};
class ResourceConfigValue {
@@ -121,7 +137,7 @@
Maybe<AllowNew> allow_new;
// The declarations of this resource as overlayable for RROs
- Maybe<Overlayable> overlayable;
+ Maybe<OverlayableItem> overlayable_item;
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -251,9 +267,9 @@
bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
const ResourceId& res_id, IDiagnostics* diag);
- bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+ bool SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
IDiagnostics *diag);
- bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+ bool SetOverlayableMangled(const ResourceNameRef& name, const OverlayableItem& overlayable,
IDiagnostics* diag);
bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -328,7 +344,7 @@
bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
NameValidator name_validator, IDiagnostics* diag);
- bool SetOverlayableImpl(const ResourceNameRef &name, const Overlayable &overlayable,
+ bool SetOverlayableImpl(const ResourceNameRef &name, const OverlayableItem& overlayable,
NameValidator name_validator, IDiagnostics *diag);
bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 31095c4..a733134 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -244,48 +244,90 @@
TEST(ResourceTableTest, SetOverlayable) {
ResourceTable table;
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kProduct;
- overlayable.policies |= Overlayable::Policy::kProductServices;
- overlayable.comment = "comment";
+ auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
+ Source("res/values/overlayable.xml", 40));
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
+ overlayable_item.comment = "comment";
+ overlayable_item.source = Source("res/values/overlayable.xml", 42);
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- ASSERT_THAT(result_overlayable.comment, StrEq("comment"));
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
- | Overlayable::Policy::kProductServices));
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kProductServices));
+ ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
+ EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.source.line, 42);
}
-TEST(ResourceTableTest, AddDuplicateOverlayableSamePolicyFail) {
+TEST(ResourceTableTest, SetMultipleOverlayableResources) {
+ ResourceTable table;
+
+ const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+ auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
+ OverlayableItem overlayable(group);
+ overlayable.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
+
+ const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+ OverlayableItem overlayable2(group);
+ overlayable2.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
+
+ const ResourceName baz = test::ParseNameOrDie("android:string/baz");
+ OverlayableItem overlayable3(group);
+ overlayable3.policies = OverlayableItem::Policy::kVendor;
+ ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
+ ResourceTable table;
+
+ const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
+ overlayable_item.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
+
+ const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+ OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2", "overlay://theme"));
+ overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+ ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableSameResourcesFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- Overlayable overlayable{};
- overlayable.policies = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+ OverlayableItem overlayable_item(overlayable);
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
- Overlayable overlayable2{};
- overlayable2.policies = Overlayable::Policy::kProduct;
- ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+ OverlayableItem overlayable_item2(overlayable);
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
}
-TEST(ResourceTableTest, AddDuplicateOverlayableDifferentPolicyFail) {
+TEST(ResourceTableTest, SetOverlayableSameResourcesDifferentNameFail) {
ResourceTable table;
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- Overlayable overlayable{};
- overlayable.policies = Overlayable::Policy::kProduct;
- ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+ OverlayableItem overlayable_item(overlayable);
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
- Overlayable overlayable2{};
- overlayable2.policies = Overlayable::Policy::kVendor;
- ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+ auto overlayable2 = std::make_shared<Overlayable>("Other", "overlay://theme");
+ OverlayableItem overlayable_item2(overlayable2);
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
}
TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 81a2c2e..da541be 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -49,6 +49,9 @@
// Resource definitions corresponding to an Android package.
repeated Package package = 2;
+
+ // The <overlayable> declarations within the resource table.
+ repeated Overlayable overlayable = 3;
}
// A package ID in the range [0x00, 0xff].
@@ -133,8 +136,20 @@
string comment = 2;
}
-// Represents a declaration that a resource is overayable at runtime.
+// Represents a set of overlayable resources.
message Overlayable {
+ // The name of the <overlyabale>.
+ string name = 1;
+
+ // The location of the <overlyabale> declaration in the source.
+ Source source = 2;
+
+ // The component responsible for enabling and disabling overlays targeting this <overlayable>.
+ string actor = 3;
+}
+
+// Represents an overlayable <item> declaration within an <overlayable> tag.
+message OverlayableItem {
enum Policy {
PUBLIC = 0;
SYSTEM = 1;
@@ -143,14 +158,18 @@
PRODUCT_SERVICES = 4;
}
- // Where this declaration was defined in source.
+ // The location of the <item> declaration in source.
Source source = 1;
// Any comment associated with the declaration.
string comment = 2;
- // The policy defined in the overlayable declaration.
+ // The policy defined by the enclosing <policy> tag of this <item>.
repeated Policy policy = 3;
+
+ // The index into overlayable list that points to the <overlayable> tag that contains
+ // this <item>.
+ uint32 overlayable_idx = 4;
}
// An entry ID in the range [0x0000, 0xffff].
@@ -180,7 +199,7 @@
AllowNew allow_new = 4;
// Whether this resource can be overlaid by a runtime resource overlay (RRO).
- Overlayable overlayable = 5;
+ OverlayableItem overlayable_item = 5;
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 11a4074..e17fb47 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -43,8 +43,10 @@
PERMISSION_ATTR = 0x01010006,
EXPORTED_ATTR = 0x01010010,
GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
+ PRIORITY_ATTR = 0x0101001c,
RESOURCE_ATTR = 0x01010025,
DEBUGGABLE_ATTR = 0x0101000f,
+ TARGET_PACKAGE_ATTR = 0x01010021,
VALUE_ATTR = 0x01010024,
VERSION_CODE_ATTR = 0x0101021b,
VERSION_NAME_ATTR = 0x0101021c,
@@ -77,8 +79,11 @@
ISGAME_ATTR = 0x10103f4,
VERSION_ATTR = 0x01010519,
CERT_DIGEST_ATTR = 0x01010548,
- REQUIRED_FEATURE_ATTR = 0x1010557,
- REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+ REQUIRED_FEATURE_ATTR = 0x01010557,
+ REQUIRED_NOT_FEATURE_ATTR = 0x01010558,
+ IS_STATIC_ATTR = 0x0101055a,
+ REQUIRED_SYSTEM_PROPERTY_NAME_ATTR = 0x01010565,
+ REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR = 0x01010566,
COMPILE_SDK_VERSION_ATTR = 0x01010572,
COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
VERSION_MAJOR_ATTR = 0x01010577,
@@ -1586,6 +1591,44 @@
}
};
+
+/** Represents <overlay> elements. **/
+class Overlay : public ManifestExtractor::Element {
+ public:
+ Overlay() = default;
+ const std::string* target_package = nullptr;
+ int priority;
+ bool is_static;
+ const std::string* required_property_name = nullptr;
+ const std::string* required_property_value = nullptr;
+
+ void Extract(xml::Element* element) override {
+ target_package = GetAttributeString(FindAttribute(element, TARGET_PACKAGE_ATTR));
+ priority = GetAttributeIntegerDefault(FindAttribute(element, PRIORITY_ATTR), 0);
+ is_static = GetAttributeIntegerDefault(FindAttribute(element, IS_STATIC_ATTR), false) != 0;
+ required_property_name = GetAttributeString(
+ FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_NAME_ATTR));
+ required_property_value = GetAttributeString(
+ FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR));
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(StringPrintf("overlay:"));
+ if (target_package) {
+ printer->Print(StringPrintf(" targetPackage='%s'", target_package->c_str()));
+ }
+ printer->Print(StringPrintf(" priority='%d'", priority));
+ printer->Print(StringPrintf(" isStatic='%s'", is_static ? "true" : "false"));
+ if (required_property_name) {
+ printer->Print(StringPrintf(" requiredPropertyName='%s'", required_property_name->c_str()));
+ }
+ if (required_property_value) {
+ printer->Print(StringPrintf(" requiredPropertyValue='%s'", required_property_value->c_str()));
+ }
+ printer->Print("\n");
+ }
+};
+
/** * Represents <package-verifier> elements. **/
class PackageVerifier : public ManifestExtractor::Element {
public:
@@ -2166,6 +2209,7 @@
{"meta-data", std::is_base_of<MetaData, T>::value},
{"manifest", std::is_base_of<Manifest, T>::value},
{"original-package", std::is_base_of<OriginalPackage, T>::value},
+ {"overlay", std::is_base_of<Overlay, T>::value},
{"package-verifier", std::is_base_of<PackageVerifier, T>::value},
{"permission", std::is_base_of<Permission, T>::value},
{"provider", std::is_base_of<Provider, T>::value},
@@ -2215,6 +2259,7 @@
{"manifest", &CreateType<Manifest>},
{"meta-data", &CreateType<MetaData>},
{"original-package", &CreateType<OriginalPackage>},
+ {"overlay", &CreateType<Overlay>},
{"package-verifier", &CreateType<PackageVerifier>},
{"permission", &CreateType<Permission>},
{"provider", &CreateType<Provider>},
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 61ebd4e..c496ff0 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -434,6 +434,8 @@
return false;
}
+ auto overlayable = std::make_shared<Overlayable>();
+
ResChunkPullParser parser(GetChunkData(chunk),
GetChunkDataLen(chunk));
while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
@@ -441,25 +443,25 @@
const ResTable_overlayable_policy_header* policy_header =
ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
- Overlayable::PolicyFlags policies = Overlayable::Policy::kNone;
+ OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
- policies |= Overlayable::Policy::kPublic;
+ policies |= OverlayableItem::Policy::kPublic;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
- policies |= Overlayable::Policy::kSystem;
+ policies |= OverlayableItem::Policy::kSystem;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
- policies |= Overlayable::Policy::kVendor;
+ policies |= OverlayableItem::Policy::kVendor;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
- policies |= Overlayable::Policy::kProduct;
+ policies |= OverlayableItem::Policy::kProduct;
}
if (policy_header->policy_flags
& ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
- policies |= Overlayable::Policy::kProductServices;
+ policies |= OverlayableItem::Policy::kProductServices;
}
const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
@@ -478,10 +480,10 @@
return false;
}
- Overlayable overlayable{};
- overlayable.source = source_.WithLine(0);
- overlayable.policies = policies;
- if (!table_->SetOverlayable(iter->second, overlayable, diag_)) {
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.source = source_.WithLine(0);
+ overlayable_item.policies = policies;
+ if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
return false;
}
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 200e2d4..931d57b 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -429,29 +429,29 @@
CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
for (auto& entry : type->entries) {
CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
- if (!entry->overlayable) {
+ if (!entry->overlayable_item) {
continue;
}
- Overlayable overlayable = entry->overlayable.value();
- uint32_t policy_flags = Overlayable::Policy::kNone;
- if (overlayable.policies & Overlayable::Policy::kPublic) {
+ OverlayableItem& overlayable = entry->overlayable_item.value();
+ uint32_t policy_flags = OverlayableItem::Policy::kNone;
+ if (overlayable.policies & OverlayableItem::Policy::kPublic) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
}
- if (overlayable.policies & Overlayable::Policy::kSystem) {
+ if (overlayable.policies & OverlayableItem::Policy::kSystem) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
}
- if (overlayable.policies & Overlayable::Policy::kVendor) {
+ if (overlayable.policies & OverlayableItem::Policy::kVendor) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
}
- if (overlayable.policies & Overlayable::Policy::kProduct) {
+ if (overlayable.policies & OverlayableItem::Policy::kProduct) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
}
- if (overlayable.policies & Overlayable::Policy::kProductServices) {
+ if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
}
- if (overlayable.policies == Overlayable::Policy::kNone) {
+ if (overlayable.policies == OverlayableItem::Policy::kNone) {
// Encode overlayable entries defined without a policy as publicly overlayable
policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
}
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e99ab1f..a5fb6fd 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,17 +628,17 @@
}
TEST_F(TableFlattenerTest, FlattenOverlayable) {
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kProduct;
- overlayable.policies |= Overlayable::Policy::kSystem;
- overlayable.policies |= Overlayable::Policy::kVendor;
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+ overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item.policies |= OverlayableItem::Policy::kVendor;
std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name, ResourceId(0x7f020000))
- .SetOverlayable(name, overlayable)
+ .SetOverlayable(name, overlayable_item)
.Build();
ResourceTable output_table;
@@ -647,45 +647,46 @@
auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
- | Overlayable::Policy::kVendor
- | Overlayable::Policy::kProduct);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kVendor
+ | OverlayableItem::Policy::kProduct);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
- std::string name_zero = "com.app.test:integer/overlayable_zero";
- Overlayable overlayable_zero{};
- overlayable_zero.policies |= Overlayable::Policy::kProduct;
- overlayable_zero.policies |= Overlayable::Policy::kSystem;
- overlayable_zero.policies |= Overlayable::Policy::kProductServices;
+ auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
+ std::string name_zero = "com.app.test:integer/overlayable_zero_item";
+ OverlayableItem overlayable_zero_item(overlayable);
+ overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices;
- std::string name_one = "com.app.test:integer/overlayable_one";
- Overlayable overlayable_one{};
- overlayable_one.policies |= Overlayable::Policy::kPublic;
- overlayable_one.policies |= Overlayable::Policy::kProductServices;
+ std::string name_one = "com.app.test:integer/overlayable_one_item";
+ OverlayableItem overlayable_one_item(overlayable);
+ overlayable_one_item.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices;
- std::string name_two = "com.app.test:integer/overlayable_two";
- Overlayable overlayable_two{};
- overlayable_two.policies |= Overlayable::Policy::kProduct;
- overlayable_two.policies |= Overlayable::Policy::kSystem;
- overlayable_two.policies |= Overlayable::Policy::kVendor;
+ std::string name_two = "com.app.test:integer/overlayable_two_item";
+ OverlayableItem overlayable_two_item(overlayable);
+ overlayable_two_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_two_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_two_item.policies |= OverlayableItem::Policy::kVendor;
- std::string name_three = "com.app.test:integer/overlayable_three";
- Overlayable overlayable_three{};
+ std::string name_three = "com.app.test:integer/overlayable_three_item";
+ OverlayableItem overlayable_three_item(overlayable);
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
- .SetOverlayable(name_zero, overlayable_zero)
+ .SetOverlayable(name_zero, overlayable_zero_item)
.AddSimple(name_one, ResourceId(0x7f020001))
- .SetOverlayable(name_one, overlayable_one)
+ .SetOverlayable(name_one, overlayable_one_item)
.AddSimple(name_two, ResourceId(0x7f020002))
- .SetOverlayable(name_two, overlayable_two)
+ .SetOverlayable(name_two, overlayable_two_item)
.AddSimple(name_three, ResourceId(0x7f020003))
- .SetOverlayable(name_three, overlayable_three)
+ .SetOverlayable(name_three, overlayable_three_item)
.Build();
ResourceTable output_table;
@@ -694,35 +695,35 @@
auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
- | Overlayable::Policy::kProduct
- | Overlayable::Policy::kProductServices);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic
- | Overlayable::Policy::kProductServices);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic
+ | OverlayableItem::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
- | Overlayable::Policy::kProduct
- | Overlayable::Policy::kVendor);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kVendor);
search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
}
} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index cf2ab0f..6b5746d 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -373,9 +373,44 @@
return Visibility::Level::kUndefined;
}
+bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable,
+ const android::ResStringPool& src_pool,
+ OverlayableItem* out_overlayable, std::string* out_error) {
+ for (const int policy : pb_overlayable.policy()) {
+ switch (policy) {
+ case pb::OverlayableItem::PUBLIC:
+ out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+ break;
+ case pb::OverlayableItem::SYSTEM:
+ out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+ break;
+ case pb::OverlayableItem::VENDOR:
+ out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+ break;
+ case pb::OverlayableItem::PRODUCT:
+ out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+ break;
+ case pb::OverlayableItem::PRODUCT_SERVICES:
+ out_overlayable->policies |= OverlayableItem::Policy::kProductServices;
+ break;
+ default:
+ *out_error = "unknown overlayable policy";
+ return false;
+ }
+ }
+
+ if (pb_overlayable.has_source()) {
+ DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &out_overlayable->source);
+ }
+
+ out_overlayable->comment = pb_overlayable.comment();
+ return true;
+}
+
static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
- io::IFileCollection* files, ResourceTable* out_table,
- std::string* out_error) {
+ io::IFileCollection* files,
+ const std::vector<std::shared_ptr<Overlayable>>& overlayables,
+ ResourceTable* out_table, std::string* out_error) {
Maybe<uint8_t> id;
if (pb_package.has_package_id()) {
id = static_cast<uint8_t>(pb_package.package_id().id());
@@ -437,39 +472,22 @@
entry->allow_new = std::move(allow_new);
}
- if (pb_entry.has_overlayable()) {
- Overlayable overlayable{};
-
- const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
- for (const int policy : pb_overlayable.policy()) {
- switch (policy) {
- case pb::Overlayable::PUBLIC:
- overlayable.policies |= Overlayable::Policy::kPublic;
- break;
- case pb::Overlayable::SYSTEM:
- overlayable.policies |= Overlayable::Policy::kSystem;
- break;
- case pb::Overlayable::VENDOR:
- overlayable.policies |= Overlayable::Policy::kVendor;
- break;
- case pb::Overlayable::PRODUCT:
- overlayable.policies |= Overlayable::Policy::kProduct;
- break;
- case pb::Overlayable::PRODUCT_SERVICES:
- overlayable.policies |= Overlayable::Policy::kProductServices;
- break;
- default:
- *out_error = "unknown overlayable policy";
- return false;
- }
+ if (pb_entry.has_overlayable_item()) {
+ // Find the overlayable to which this item belongs
+ pb::OverlayableItem pb_overlayable_item = pb_entry.overlayable_item();
+ if (pb_overlayable_item.overlayable_idx() >= overlayables.size()) {
+ *out_error = android::base::StringPrintf("invalid overlayable_idx value %d",
+ pb_overlayable_item.overlayable_idx());
+ return false;
}
- if (pb_overlayable.has_source()) {
- DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
+ OverlayableItem overlayable_item(overlayables[pb_overlayable_item.overlayable_idx()]);
+ if (!DeserializeOverlayableItemFromPb(pb_overlayable_item, src_pool, &overlayable_item,
+ out_error)) {
+ return false;
}
- overlayable.comment = pb_overlayable.comment();
- entry->overlayable = overlayable;
+ entry->overlayable_item = std::move(overlayable_item);
}
ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
@@ -522,8 +540,19 @@
}
}
+ // Deserialize the overlayable groups of the table
+ std::vector<std::shared_ptr<Overlayable>> overlayables;
+ for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) {
+ auto group = std::make_shared<Overlayable>(pb_overlayable.name(), pb_overlayable.actor());
+ if (pb_overlayable.has_source()) {
+ DeserializeSourceFromPb(pb_overlayable.source(), source_pool, &group->source);
+ }
+ overlayables.push_back(group);
+ }
+
for (const pb::Package& pb_package : pb_table.package()) {
- if (!DeserializePackageFromPb(pb_package, source_pool, files, out_table, out_error)) {
+ if (!DeserializePackageFromPb(pb_package, source_pool, files, overlayables, out_table,
+ out_error)) {
return false;
}
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 70bf868..76fbb46 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -272,9 +272,57 @@
out_pb_config->set_sdk_version(config.sdkVersion);
}
+static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item,
+ std::vector<Overlayable*>& serialized_overlayables,
+ StringPool* source_pool, pb::Entry* pb_entry,
+ pb::ResourceTable* pb_table) {
+ // Retrieve the index of the overlayable in the list of groups that have already been serialized.
+ size_t i;
+ for (i = 0 ; i < serialized_overlayables.size(); i++) {
+ if (overlayable_item.overlayable.get() == serialized_overlayables[i]) {
+ break;
+ }
+ }
+
+ // Serialize the overlayable if it has not been serialized already.
+ if (i == serialized_overlayables.size()) {
+ serialized_overlayables.push_back(overlayable_item.overlayable.get());
+ pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
+ pb_overlayable->set_name(overlayable_item.overlayable->name);
+ pb_overlayable->set_actor(overlayable_item.overlayable->actor);
+ SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
+ pb_overlayable->mutable_source());
+ }
+
+ pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
+ pb_overlayable_item->set_overlayable_idx(i);
+
+ if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kProductServices) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT_SERVICES);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
+ }
+ if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
+ }
+
+ SerializeSourceToPb(overlayable_item.source, source_pool,
+ pb_overlayable_item->mutable_source());
+ pb_overlayable_item->set_comment(overlayable_item.comment);
+}
+
void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
IDiagnostics* diag) {
StringPool source_pool;
+
+ std::vector<Overlayable*> overlayables;
for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
pb::Package* pb_package = out_table->add_package();
if (package->id) {
@@ -310,29 +358,9 @@
pb_allow_new->set_comment(entry->allow_new.value().comment);
}
- if (entry->overlayable) {
- pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
-
- Overlayable overlayable = entry->overlayable.value();
- if (overlayable.policies & Overlayable::Policy::kPublic) {
- pb_overlayable->add_policy(pb::Overlayable::PUBLIC);
- }
- if (overlayable.policies & Overlayable::Policy::kProduct) {
- pb_overlayable->add_policy(pb::Overlayable::PRODUCT);
- }
- if (overlayable.policies & Overlayable::Policy::kProductServices) {
- pb_overlayable->add_policy(pb::Overlayable::PRODUCT_SERVICES);
- }
- if (overlayable.policies & Overlayable::Policy::kSystem) {
- pb_overlayable->add_policy(pb::Overlayable::SYSTEM);
- }
- if (overlayable.policies & Overlayable::Policy::kVendor) {
- pb_overlayable->add_policy(pb::Overlayable::VENDOR);
- }
-
- SerializeSourceToPb(overlayable.source, &source_pool,
- pb_overlayable->mutable_source());
- pb_overlayable->set_comment(overlayable.comment);
+ if (entry->overlayable_item) {
+ SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables, &source_pool,
+ pb_entry, out_table);
}
for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index fb913f40..4a3c1b8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,8 +93,11 @@
util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
// Make an overlayable resource.
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>(
+ "OverlayableName", "overlay://theme", Source("res/values/overlayable.xml", 40)));
+ overlayable_item.source = Source("res/values/overlayable.xml", 42);
ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
- Overlayable{}, test::GetDiagnostics()));
+ overlayable_item, test::GetDiagnostics()));
pb::ResourceTable pb_table;
SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
@@ -160,9 +163,15 @@
new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
- ASSERT_TRUE(search_result.value().entry->overlayable);
- EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
- Eq(Overlayable::Policy::kNone));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("OverlayableName"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+ EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
}
TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -503,26 +512,31 @@
}
TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
- Overlayable overlayable_foo{};
- overlayable_foo.policies |= Overlayable::Policy::kSystem;
- overlayable_foo.policies |= Overlayable::Policy::kProduct;
+ OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
+ "CustomizableResources", "overlay://customization"));
+ overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
- Overlayable overlayable_bar{};
- overlayable_bar.policies |= Overlayable::Policy::kProductServices;
- overlayable_bar.policies |= Overlayable::Policy::kVendor;
+ OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
+ "TaskBar", "overlay://theme"));
+ overlayable_item_bar.policies |= OverlayableItem::Policy::kProductServices;
+ overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
- Overlayable overlayable_baz{};
- overlayable_baz.policies |= Overlayable::Policy::kPublic;
+ OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
+ "FontPack", "overlay://theme"));
+ overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
- Overlayable overlayable_biz{};
+ OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
+ "Other", "overlay://customization"));
+ overlayable_item_biz.comment ="comment";
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetOverlayable("com.app.a:bool/foo", overlayable_foo)
- .SetOverlayable("com.app.a:bool/bar", overlayable_bar)
- .SetOverlayable("com.app.a:bool/baz", overlayable_baz)
- .SetOverlayable("com.app.a:bool/biz", overlayable_biz)
+ .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
+ .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
+ .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+ .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
.AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
.Build();
@@ -538,33 +552,41 @@
Maybe<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kSystem
- | Overlayable::Policy::kProduct));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
+ | OverlayableItem::Policy::kProduct));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProductServices
- | Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices
+ | OverlayableItem::Policy::kVendor));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kPublic);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kNone);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
+ EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(overlayable_item.comment, Eq("comment"));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
ASSERT_TRUE(search_result);
- ASSERT_FALSE(search_result.value().entry->overlayable);
+ ASSERT_FALSE(search_result.value().entry->overlayable_item);
}
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8cbc037..c2340ba 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -374,8 +374,8 @@
}
// Ensure that definitions for values declared as overlayable exist
- if (entry->overlayable && entry->values.empty()) {
- context->GetDiagnostics()->Error(DiagMessage(entry->overlayable.value().source)
+ if (entry->overlayable_item && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
<< "no definition for overlayable symbol '"
<< name << "'");
error = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22e1723..cc9fed5 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -134,18 +134,18 @@
dst_entry->allow_new = std::move(src_entry->allow_new);
}
- if (src_entry->overlayable) {
- if (dst_entry->overlayable) {
+ if (src_entry->overlayable_item) {
+ if (dst_entry->overlayable_item) {
// Do not allow a resource with an overlayable declaration to have that overlayable
// declaration redefined
- context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+ context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable_item.value().source)
<< "duplicate overlayable declaration for resource '"
<< src_entry->name << "'");
- context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+ context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable_item.value().source)
<< "previous declaration here");
return false;
} else {
- dst_entry->overlayable = std::move(src_entry->overlayable);
+ dst_entry->overlayable_item = std::move(src_entry->overlayable_item);
}
}
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 17b2a83..921d634 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -437,14 +437,16 @@
}
TEST_F(TableMergerTest, SetOverlayable) {
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kProduct;
- overlayable.policies |= Overlayable::Policy::kVendor;
+ auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item.policies |= OverlayableItem::Policy::kVendor;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable)
+ .SetOverlayable("bool/foo", overlayable_item)
.Build();
std::unique_ptr<ResourceTable> table_b =
@@ -463,26 +465,30 @@
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
- | Overlayable::Policy::kVendor));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+ | OverlayableItem::Policy::kVendor));
}
TEST_F(TableMergerTest, SetOverlayableLater) {
+ auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
.AddSimple("bool/foo")
.Build();
- Overlayable overlayable{};
- overlayable.policies |= Overlayable::Policy::kPublic;
- overlayable.policies |= Overlayable::Policy::kProductServices;
+ OverlayableItem overlayable_item(overlayable);
+ overlayable_item.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable)
+ .SetOverlayable("bool/foo", overlayable_item)
.Build();
ResourceTable final_table;
@@ -495,27 +501,33 @@
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
- ASSERT_TRUE(search_result.value().entry->overlayable);
- Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
- EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kPublic
- | Overlayable::Policy::kProductServices));
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+ EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
+ | OverlayableItem::Policy::kProductServices));
}
-TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
- Overlayable overlayable_first{};
- overlayable_first.policies |= Overlayable::Policy::kProduct;
+TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
+ auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
+ OverlayableItem overlayable_item_first(overlayable_first);
+ overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable_first)
+ .SetOverlayable("bool/foo", overlayable_item_first)
.Build();
- Overlayable overlayable_second{};
- overlayable_second.policies |= Overlayable::Policy::kProduct;
+ auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
+ "overlay://theme");
+ OverlayableItem overlayable_item_second(overlayable_second);
+ overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable_second)
+ .SetOverlayable("bool/foo", overlayable_item_second)
.Build();
ResourceTable final_table;
@@ -526,21 +538,24 @@
ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
}
-TEST_F(TableMergerTest, SetOverlayableDifferentPolicesFail) {
- Overlayable overlayable_first{};
- overlayable_first.policies |= Overlayable::Policy::kVendor;
+TEST_F(TableMergerTest, SameResourceSameNameFail) {
+ auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+ "overlay://customization");
+
+ OverlayableItem overlayable_item_first(overlayable);
+ overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo",overlayable_first)
+ .SetOverlayable("bool/foo", overlayable_item_first)
.Build();
- Overlayable overlayable_second{};
- overlayable_second.policies |= Overlayable::Policy::kProduct;
+ OverlayableItem overlayable_item_second(overlayable);
+ overlayable_item_second.policies |= OverlayableItem::Policy::kSystem;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
- .SetOverlayable("bool/foo", overlayable_second)
+ .SetOverlayable("bool/foo", overlayable_item_second)
.Build();
ResourceTable final_table;
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 9c5b5d3..24cd5ba 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,7 +248,7 @@
if (!split_entry->id) {
split_entry->id = entry->id;
split_entry->visibility = entry->visibility;
- split_entry->overlayable = entry->overlayable;
+ split_entry->overlayable_item = entry->overlayable_item;
}
// Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 884ec38..9a93f2a 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -136,7 +136,7 @@
}
ResourceTableBuilder& ResourceTableBuilder::SetOverlayable(const StringPiece& name,
- const Overlayable& overlayable) {
+ const OverlayableItem& overlayable) {
ResourceName res_name = ParseNameOrDie(name);
CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index a120484..c971a1b 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -74,7 +74,7 @@
ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
Visibility::Level level, bool allow_new = false);
ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
- const Overlayable& overlayable);
+ const OverlayableItem& overlayable);
StringPool* string_pool();
std::unique_ptr<ResourceTable> Build();
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index b5a990e..6476abd 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -209,24 +209,42 @@
return self.raw
-def _parse_stream(f, clazz_cb=None, base_f=None):
+def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None,
+ in_classes_with_base=[]):
api = {}
+ in_classes_with_base = _retry_iterator(in_classes_with_base)
if base_f:
- base_classes = _parse_stream_to_generator(base_f)
+ base_classes = _retry_iterator(_parse_stream_to_generator(base_f))
else:
base_classes = []
- for clazz in _parse_stream_to_generator(f):
- base_class = _parse_to_matching_class(base_classes, clazz)
- if base_class:
- clazz.merge_from(base_class)
-
+ def handle_class(clazz):
if clazz_cb:
clazz_cb(clazz)
else: # In callback mode, don't keep track of the full API
api[clazz.fullname] = clazz
+ def handle_missed_classes_with_base(clazz):
+ for c in _yield_until_matching_class(in_classes_with_base, clazz):
+ base_class = _skip_to_matching_class(base_classes, c)
+ if base_class:
+ handle_class(base_class)
+
+ for clazz in _parse_stream_to_generator(f):
+ # Before looking at clazz, let's see if there's some classes that were not present, but
+ # may have an entry in the base stream.
+ handle_missed_classes_with_base(clazz)
+
+ base_class = _skip_to_matching_class(base_classes, clazz)
+ if base_class:
+ clazz.merge_from(base_class)
+ if out_classes_with_base is not None:
+ out_classes_with_base.append(clazz)
+ handle_class(clazz)
+
+ handle_missed_classes_with_base(None)
+
return api
def _parse_stream_to_generator(f):
@@ -257,18 +275,22 @@
elif raw.startswith(" field"):
clazz.fields.append(Field(clazz, line, raw, blame))
elif raw.startswith(" }") and clazz:
- while True:
- retry = yield clazz
- if not retry:
- break
- # send() was called, asking us to redeliver clazz on next(). Still need to yield
- # a dummy value to the send() first though.
- if (yield "Returning clazz on next()"):
- raise TypeError("send() must be followed by next(), not send()")
+ yield clazz
+def _retry_iterator(it):
+ """Wraps an iterator, such that calling send(True) on it will redeliver the same element"""
+ for e in it:
+ while True:
+ retry = yield e
+ if not retry:
+ break
+ # send() was called, asking us to redeliver clazz on next(). Still need to yield
+ # a dummy value to the send() first though.
+ if (yield "Returning clazz on next()"):
+ raise TypeError("send() must be followed by next(), not send()")
-def _parse_to_matching_class(classes, needle):
- """Takes a classes generator and parses it until it returns the class we're looking for
+def _skip_to_matching_class(classes, needle):
+ """Takes a classes iterator and consumes entries until it returns the class we're looking for
This relies on classes being sorted by package and class name."""
@@ -276,8 +298,8 @@
if clazz.pkg.name < needle.pkg.name:
# We haven't reached the right package yet
continue
- if clazz.name < needle.name:
- # We haven't reached the right class yet
+ if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+ # We're in the right package, but not the right class yet
continue
if clazz.fullname == needle.fullname:
return clazz
@@ -285,6 +307,28 @@
classes.send(clazz)
return None
+def _yield_until_matching_class(classes, needle):
+ """Takes a class iterator and yields entries it until it reaches the class we're looking for.
+
+ This relies on classes being sorted by package and class name."""
+
+ for clazz in classes:
+ if needle is None:
+ yield clazz
+ elif clazz.pkg.name < needle.pkg.name:
+ # We haven't reached the right package yet
+ yield clazz
+ elif clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+ # We're in the right package, but not the right class yet
+ yield clazz
+ elif clazz.fullname == needle.fullname:
+ # Class found, abort.
+ return
+ else:
+ # We ran past the right class. Send it back into the iterator, then abort.
+ classes.send(clazz)
+ return
+
class Failure():
def __init__(self, sig, clazz, detail, error, rule, msg):
self.sig = sig
@@ -1543,12 +1587,14 @@
verify_singleton(clazz)
-def examine_stream(stream, base_stream=None):
+def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None):
"""Find all style issues in the given API stream."""
global failures, noticed
failures = {}
noticed = {}
- _parse_stream(stream, examine_clazz, base_f=base_stream)
+ _parse_stream(stream, examine_clazz, base_f=base_stream,
+ in_classes_with_base=in_classes_with_base,
+ out_classes_with_base=out_classes_with_base)
return (failures, noticed)
@@ -1734,19 +1780,24 @@
show_stats(cur, prev)
sys.exit()
+ classes_with_base = []
+
with current_file as f:
if base_current_file:
with base_current_file as base_f:
- cur_fail, cur_noticed = examine_stream(f, base_f)
+ cur_fail, cur_noticed = examine_stream(f, base_f,
+ out_classes_with_base=classes_with_base)
else:
- cur_fail, cur_noticed = examine_stream(f)
+ cur_fail, cur_noticed = examine_stream(f, out_classes_with_base=classes_with_base)
+
if not previous_file is None:
with previous_file as f:
if base_previous_file:
with base_previous_file as base_f:
- prev_fail, prev_noticed = examine_stream(f, base_f)
+ prev_fail, prev_noticed = examine_stream(f, base_f,
+ in_classes_with_base=classes_with_base)
else:
- prev_fail, prev_noticed = examine_stream(f)
+ prev_fail, prev_noticed = examine_stream(f, in_classes_with_base=classes_with_base)
# ignore errors from previous API level
for p in prev_fail:
diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh
new file mode 100755
index 0000000..8538a3d
--- /dev/null
+++ b/tools/apilint/apilint_sha_system.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# 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.
+
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+ python tools/apilint/apilint.py \
+ --base-current <(git show $1:api/current.txt) \
+ --base-previous <(git show $1^:api/current.txt) \
+ <(git show $1:api/system-current.txt) \
+ <(git show $1^:api/system-current.txt)
+fi
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
new file mode 100644
index 0000000..ece69a9
--- /dev/null
+++ b/tools/apilint/apilint_test.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+# 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.
+
+import unittest
+
+import apilint
+
+def cls(pkg, name):
+ return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999,
+ "public final class %s {" % name, None)
+
+_ri = apilint._retry_iterator
+
+c1 = cls("android.app", "ActivityManager")
+c2 = cls("android.app", "Notification")
+c3 = cls("android.app", "Notification.Action")
+c4 = cls("android.graphics", "Bitmap")
+
+class UtilTests(unittest.TestCase):
+ def test_retry_iterator(self):
+ it = apilint._retry_iterator([1, 2, 3, 4])
+ self.assertEqual(it.next(), 1)
+ self.assertEqual(it.next(), 2)
+ self.assertEqual(it.next(), 3)
+ it.send("retry")
+ self.assertEqual(it.next(), 3)
+ self.assertEqual(it.next(), 4)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_retry_iterator_one(self):
+ it = apilint._retry_iterator([1])
+ self.assertEqual(it.next(), 1)
+ it.send("retry")
+ self.assertEqual(it.next(), 1)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_retry_iterator_one(self):
+ it = apilint._retry_iterator([1])
+ self.assertEqual(it.next(), 1)
+ it.send("retry")
+ self.assertEqual(it.next(), 1)
+ with self.assertRaises(StopIteration):
+ it.next()
+
+ def test_skip_to_matching_class_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(apilint._skip_to_matching_class(it, c3),
+ c3)
+ self.assertEqual(it.next(), c4)
+
+ def test_skip_to_matching_class_not_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(apilint._skip_to_matching_class(it, cls("android.content", "ContentProvider")),
+ None)
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, c3)),
+ [c1, c2])
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_not_found(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, cls("android.content", "ContentProvider"))),
+ [c1, c2, c3])
+ self.assertEqual(it.next(), c4)
+
+ def test_yield_until_matching_class_None(self):
+ it = _ri([c1, c2, c3, c4])
+ self.assertEquals(list(apilint._yield_until_matching_class(it, None)),
+ [c1, c2, c3, c4])
+
+
+faulty_current_txt = """
+package android.app {
+ public final class Activity {
+ }
+
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.os.Parcel);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+}
+""".split('\n')
+
+ok_current_txt = """
+package android.app {
+ public final class Activity {
+ }
+
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+}
+""".split('\n')
+
+system_current_txt = """
+package android.app {
+ public final class WallpaperColors implements android.os.Parcelable {
+ method public int getSomething();
+ }
+}
+""".split('\n')
+
+
+
+class BaseFileTests(unittest.TestCase):
+ def test_base_file_avoids_errors(self):
+ failures, _ = apilint.examine_stream(system_current_txt, ok_current_txt)
+ self.assertEquals(failures, {})
+
+ def test_class_with_base_finds_same_errors(self):
+ failures_with_classes_with_base, _ = apilint.examine_stream("", faulty_current_txt,
+ in_classes_with_base=[cls("android.app", "WallpaperColors")])
+ failures_with_system_txt, _ = apilint.examine_stream(system_current_txt, faulty_current_txt)
+
+ self.assertEquals(failures_with_classes_with_base.keys(), failures_with_system_txt.keys())
+
+ def test_classes_with_base_is_emited(self):
+ classes_with_base = []
+ _, _ = apilint.examine_stream(system_current_txt, faulty_current_txt,
+ out_classes_with_base=classes_with_base)
+ self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
+
+if __name__ == "__main__":
+ unittest.main()
\ No newline at end of file
diff --git a/tools/incident_report/printer.h b/tools/incident_report/printer.h
index ed93fa1..63e276b 100644
--- a/tools/incident_report/printer.h
+++ b/tools/incident_report/printer.h
@@ -22,7 +22,7 @@
class Out
{
public:
- Out(int fd);
+ explicit Out(int fd);
~Out();
void printf(const char* format, ...);
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index faa3547..f6c9c0e 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -453,7 +453,7 @@
map<string, bool> variableNames;
set<string> parents;
vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
- bool skip[fieldsInOrder.size()];
+ vector<bool> skip(fieldsInOrder.size());
const Destination incidentDest = getPrivacyFlags(descriptor).dest();
for (size_t i=0; i<fieldsInOrder.size(); i++) {
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 703a67b..5725f0c 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -96,6 +96,7 @@
cc_library_shared {
name: "libstatslog",
+ host_supported: true,
generated_sources: ["statslog.cpp"],
generated_headers: ["statslog.h"],
cflags: [
@@ -105,8 +106,19 @@
export_generated_headers: ["statslog.h"],
shared_libs: [
"liblog",
- "libutils",
"libcutils",
],
static_libs: ["libstatssocket"],
+ target: {
+ android: {
+ shared_libs: [
+ "libutils",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libutils",
+ ],
+ },
+ },
}
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 5192a0e..11b408f 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -106,7 +106,9 @@
fprintf(out, "#include <mutex>\n");
fprintf(out, "#include <chrono>\n");
fprintf(out, "#include <thread>\n");
+ fprintf(out, "#ifdef __ANDROID__\n");
fprintf(out, "#include <cutils/properties.h>\n");
+ fprintf(out, "#endif\n");
fprintf(out, "#include <stats_event_list.h>\n");
fprintf(out, "#include <log/log.h>\n");
fprintf(out, "#include <statslog.h>\n");
@@ -117,7 +119,11 @@
fprintf(out, "namespace util {\n");
fprintf(out, "// the single event tag id for all stats logs\n");
fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
+ fprintf(out, "#ifdef __ANDROID__\n");
fprintf(out, "const static bool kStatsdEnabled = property_get_bool(\"ro.statsd.enable\", true);\n");
+ fprintf(out, "#else\n");
+ fprintf(out, "const static bool kStatsdEnabled = false;\n");
+ fprintf(out, "#endif\n");
std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
"audio_state_changed",
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 64f8adb..d023b58 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -708,6 +708,12 @@
}
/**
+ * This Wifi configuration is expected for OSU(Online Sign Up) of Passpoint Release 2.
+ * @hide
+ */
+ public boolean osu;
+
+ /**
* @hide
* Last time the system was connected to this configuration.
*/
@@ -1645,6 +1651,7 @@
selfAdded = false;
didSelfAdd = false;
ephemeral = false;
+ osu = false;
trusted = true; // Networks are considered trusted by default.
meteredHint = false;
meteredOverride = METERED_OVERRIDE_NONE;
@@ -1755,6 +1762,7 @@
if (this.selfAdded) sbuf.append(" selfAdded");
if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
if (this.ephemeral) sbuf.append(" ephemeral");
+ if (this.osu) sbuf.append(" osu");
if (this.trusted) sbuf.append(" trusted");
if (this.meteredHint) sbuf.append(" meteredHint");
if (this.useExternalScores) sbuf.append(" useExternalScores");
@@ -2244,6 +2252,7 @@
validatedInternetAccess = source.validatedInternetAccess;
isLegacyPasspointConfig = source.isLegacyPasspointConfig;
ephemeral = source.ephemeral;
+ osu = source.osu;
trusted = source.trusted;
meteredHint = source.meteredHint;
meteredOverride = source.meteredOverride;
@@ -2340,6 +2349,7 @@
dest.writeInt(recentFailure.getAssociationStatus());
dest.writeParcelable(mRandomizedMacAddress, flags);
dest.writeInt(macRandomizationSetting);
+ dest.writeInt(osu ? 1 : 0);
}
/** Implement the Parcelable interface {@hide} */
@@ -2410,6 +2420,7 @@
config.recentFailure.setAssociationStatus(in.readInt());
config.mRandomizedMacAddress = in.readParcelable(null);
config.macRandomizationSetting = in.readInt();
+ config.osu = in.readInt() != 0;
return config;
}
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 73341ac..401b652 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -45,7 +45,7 @@
LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
core-test-rules \
guava \
mockito-target-minus-junit4 \
diff --git a/wifi/tests/AndroidManifest.xml b/wifi/tests/AndroidManifest.xml
index 4eaca2b..b6c38bc 100644
--- a/wifi/tests/AndroidManifest.xml
+++ b/wifi/tests/AndroidManifest.xml
@@ -30,7 +30,7 @@
</activity>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.net.wifi.test"
android:label="Frameworks Wifi API Tests">
</instrumentation>
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 45c7a17..cae19e4 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-tag" value="FrameworksWifiApiTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.wifi.test" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/wifi/tests/README.md b/wifi/tests/README.md
index b418abd..b0594f2 100644
--- a/wifi/tests/README.md
+++ b/wifi/tests/README.md
@@ -37,7 +37,7 @@
If you manually build and push the test APK to the device you can run tests using
```
-adb shell am instrument -w 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
```
## Adding Tests
diff --git a/wifi/tests/runtests.sh b/wifi/tests/runtests.sh
index 4e52b8f..2caf8a5 100755
--- a/wifi/tests/runtests.sh
+++ b/wifi/tests/runtests.sh
@@ -22,4 +22,4 @@
adb install -r -g "$OUT/data/app/FrameworksWifiApiTests/FrameworksWifiApiTests.apk"
adb shell am instrument --no-hidden-api-checks -w "$@" \
- 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+ 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
diff --git a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
index 0c3bf3b..917b1df 100644
--- a/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
+++ b/wifi/tests/src/android/net/wifi/ParcelUtilTest.java
@@ -22,7 +22,8 @@
import static org.junit.Assert.assertNull;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 458c43d..54ec325 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -22,15 +22,14 @@
import static org.mockito.Mockito.validateMockitoUsage;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.net.wifi.WifiScanner.ScanSettings;
+
+import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
-
/**
* Unit tests for {@link android.net.wifi.WifiScanner}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8d97307..c744f18 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -26,7 +26,8 @@
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index e569efe..6ec64ff 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -19,8 +19,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -28,7 +28,8 @@
import android.net.wifi.WifiEnterpriseConfig.Phase2;
import android.os.Parcel;
import android.security.Credentials;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
@@ -36,7 +37,6 @@
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
-
/**
* Unit tests for {@link android.net.wifi.WifiEnterpriseConfig}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index f9fb062..fb0af5f 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -21,7 +21,8 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 1001b10..c43948b 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -66,7 +66,8 @@
import android.os.Message;
import android.os.Messenger;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index f8ab8a2..2258e4d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -25,9 +25,10 @@
import android.net.NetworkRequest;
import android.os.Parcel;
import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 2505499..83627ad 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -29,7 +29,8 @@
import android.net.NetworkSpecifier;
import android.os.PatternMatcher;
import android.os.Process;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index 997282b..fdd11a3 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -29,8 +29,9 @@
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
import android.os.HandlerThread;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
@@ -44,7 +45,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
/** Unit tests for {@link WifiNetworkScoreCache}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 856f0c7..2a8df8d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -26,9 +26,10 @@
import android.net.MatchAllNetworkSpecifier;
import android.os.Parcel;
import android.os.PatternMatcher;
-import android.support.test.filters.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 5cc8217..31f501f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -19,7 +19,8 @@
import static org.junit.Assert.*;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index cf1ed8f..76bfff0 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -32,7 +32,8 @@
import android.os.Handler;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.internal.util.test.BidirectionalAsyncChannelServer;
@@ -44,7 +45,6 @@
import java.util.Arrays;
-
/**
* Unit tests for {@link android.net.wifi.WifiScanner}.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
index b58f2c7..10a37c0 100644
--- a/wifi/tests/src/android/net/wifi/WifiSsidTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
index 6ecd931..83affed 100644
--- a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
@@ -18,7 +18,7 @@
import static org.hamcrest.core.IsEqual.equalTo;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 657e5a7..4189e40 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -21,7 +21,8 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 45e1720..e882b6b 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -38,7 +38,8 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import libcore.util.HexEncoding;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
index f32fe59..d9a1d9af 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
@@ -22,7 +22,10 @@
import android.net.wifi.FakeKeys;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
import java.io.BufferedReader;
import java.io.IOException;
@@ -30,8 +33,6 @@
import java.io.InputStreamReader;
import java.util.Arrays;
-import org.junit.Test;
-
/**
* Unit tests for {@link android.net.wifi.hotspot2.ConfigParser}.
*/
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
index 89ecd0f..c7e009e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
@@ -23,7 +23,8 @@
import android.net.Uri;
import android.net.wifi.WifiSsid;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index ee5a75e..fc03e7e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -25,9 +25,10 @@
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
index 707b64f..66c595f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java
@@ -19,14 +19,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.net.wifi.hotspot2.omadm.PpsMoParser;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
-import android.support.test.filters.SmallTest;
-import android.text.TextUtils;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
index ef478c7..85d0a90 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
@@ -20,7 +20,8 @@
import android.net.wifi.hotspot2.omadm.XMLNode;
import android.net.wifi.hotspot2.omadm.XMLParser;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index c07db6c..a9d4b8f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -22,7 +22,8 @@
import android.net.wifi.EAPConstants;
import android.net.wifi.FakeKeys;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
index c7993e3..93d471a 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
@@ -20,7 +20,8 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
index 171d6ff..980b199 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
@@ -20,15 +20,15 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
index 2a7526b..0b8cd3d 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
@@ -20,17 +20,14 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
-import android.support.test.filters.SmallTest;
import android.util.Base64;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
/**
* Unit tests for {@link android.net.wifi.hotspot2.pps.UpdateParameter}.
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index 80f00a4..f61e6b7 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
index 2132b41..9e8dca4 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
@@ -21,7 +21,8 @@
import android.content.Context;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import libcore.junit.util.ResourceLeakageDetector;
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 8997ae9..afc7dff 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -31,7 +31,8 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;