Merge "Try to add some more documentation to testables"
diff --git a/Android.bp b/Android.bp
index 81d6dab..4f2e6d0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,6 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// ==== c++ proto device library ==============================
+cc_library {
+ name: "libplatformprotos",
+ host_supported: true,
+ // b/34740546, work around clang-tidy segmentation fault.
+ tidy_checks: ["-modernize*"],
+ proto: {
+ export_proto_headers: true,
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ target: {
+ host: {
+ proto: {
+ type: "full",
+ },
+ },
+ android: {
+ proto: {
+ type: "lite",
+ },
+ shared: {
+ // The proto files generate full protos, but we only use
+ // them as lite on device. This works fine for a static
+ // library, where the unused full symbols are stripped,
+ // but fails if it is linked as a standalone shared
+ // library because it is missing the full runtime.
+ enabled: false,
+ },
+ },
+ },
+
+ srcs: [
+ "core/proto/**/*.proto",
+ "libs/incident/**/*.proto",
+ ],
+}
+
subdirs = [
"libs/*",
"tools/*",
diff --git a/Android.mk b/Android.mk
index 8bb366a..c10a0cc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -114,6 +114,8 @@
core/java/android/app/backup/IRestoreObserver.aidl \
core/java/android/app/backup/IRestoreSession.aidl \
core/java/android/app/backup/ISelectBackupTransportCallback.aidl \
+ core/java/android/app/timezone/ICallback.aidl \
+ core/java/android/app/timezone/IRulesManager.aidl \
core/java/android/app/usage/ICacheQuotaService.aidl \
core/java/android/app/usage/IStorageStatsManager.aidl \
core/java/android/app/usage/IUsageStatsManager.aidl \
@@ -1478,35 +1480,6 @@
include $(BUILD_JAVA_LIBRARY)
-# ==== c++ proto device library ==============================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libplatformprotos
-# b/34740546, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -modernize*
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
-LOCAL_PROTOC_FLAGS := \
- --include_source_info \
- -Iexternal/protobuf/src
-LOCAL_SRC_FILES := \
- $(call all-proto-files-under, core/proto) \
- $(call all-proto-files-under, libs/incident/proto)
-include $(BUILD_STATIC_LIBRARY)
-
-# ==== c++ proto host library ==============================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libplatformprotos
-# b/34740546, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -modernize*
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-LOCAL_PROTOC_FLAGS := \
- --include_source_info \
- -Iexternal/protobuf/src
-LOCAL_SRC_FILES := \
- $(call all-proto-files-under, core/proto) \
- $(call all-proto-files-under, libs/incident/proto)
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
# ==== java proto host library ==============================
include $(CLEAR_VARS)
LOCAL_MODULE := platformprotos
diff --git a/api/current.txt b/api/current.txt
index 6efa792..0cb71a0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30528,7 +30528,7 @@
field public static final int M = 23; // 0x17
field public static final int N = 24; // 0x18
field public static final int N_MR1 = 25; // 0x19
- field public static final int O = 26; // 0x1a
+ field public static final int O = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -43979,6 +43979,7 @@
method public android.os.Vibrator getVibrator();
method public boolean[] hasKeys(int...);
method public boolean hasMicrophone();
+ method public boolean isEnabled();
method public boolean isVirtual();
method public boolean supportsSource(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 4124ad3..738ea33 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33245,7 +33245,7 @@
field public static final int M = 23; // 0x17
field public static final int N = 24; // 0x18
field public static final int N_MR1 = 25; // 0x19
- field public static final int O = 26; // 0x1a
+ field public static final int O = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -47526,6 +47526,7 @@
method public android.os.Vibrator getVibrator();
method public boolean[] hasKeys(int...);
method public boolean hasMicrophone();
+ method public boolean isEnabled();
method public boolean isVirtual();
method public boolean supportsSource(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index a4e1866..a1850f5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -30637,7 +30637,7 @@
field public static final int M = 23; // 0x17
field public static final int N = 24; // 0x18
field public static final int N_MR1 = 25; // 0x19
- field public static final int O = 26; // 0x1a
+ field public static final int O = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -44344,6 +44344,8 @@
public final class InputDevice implements android.os.Parcelable {
method public int describeContents();
+ method public void disable();
+ method public void enable();
method public int getControllerNumber();
method public java.lang.String getDescriptor();
method public static android.view.InputDevice getDevice(int);
@@ -44361,6 +44363,7 @@
method public android.os.Vibrator getVibrator();
method public boolean[] hasKeys(int...);
method public boolean hasMicrophone();
+ method public boolean isEnabled();
method public boolean isVirtual();
method public boolean supportsSource(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/cmds/vr/src/com/android/commands/vr/Vr.java b/cmds/vr/src/com/android/commands/vr/Vr.java
index bf97bba..b765866 100644
--- a/cmds/vr/src/com/android/commands/vr/Vr.java
+++ b/cmds/vr/src/com/android/commands/vr/Vr.java
@@ -16,7 +16,7 @@
package com.android.commands.vr;
-import android.app.CompatibilityDisplayProperties;
+import android.app.Vr2dDisplayProperties;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -39,7 +39,7 @@
private static final String COMMAND_SET_PERSISTENT_VR_MODE_ENABLED =
"set-persistent-vr-mode-enabled";
- private static final String COMMAND_SET_COMPATIBILITY_DISPLAY_PROPERTIES =
+ private static final String COMMAND_SET_VR2D_DISPLAY_PROPERTIES =
"set-display-props";
private IVrManager mVrService;
@@ -63,8 +63,8 @@
String command = nextArgRequired();
switch (command) {
- case COMMAND_SET_COMPATIBILITY_DISPLAY_PROPERTIES:
- runSetCompatibilityDisplayProperties();
+ case COMMAND_SET_VR2D_DISPLAY_PROPERTIES:
+ runSetVr2dDisplayProperties();
break;
case COMMAND_SET_PERSISTENT_VR_MODE_ENABLED:
runSetPersistentVrModeEnabled();
@@ -74,7 +74,7 @@
}
}
- private void runSetCompatibilityDisplayProperties() throws RemoteException {
+ private void runSetVr2dDisplayProperties() throws RemoteException {
String widthStr = nextArgRequired();
int width = Integer.parseInt(widthStr);
@@ -84,11 +84,11 @@
String dpiStr = nextArgRequired();
int dpi = Integer.parseInt(dpiStr);
- CompatibilityDisplayProperties compatDisplayProperties =
- new CompatibilityDisplayProperties(width, height, dpi);
+ Vr2dDisplayProperties vr2dDisplayProperties =
+ new Vr2dDisplayProperties(width, height, dpi);
try {
- mVrService.setCompatibilityDisplayProperties(compatDisplayProperties);
+ mVrService.setVr2dDisplayProperties(vr2dDisplayProperties);
} catch (RemoteException re) {
System.err.println("Error: Can't set persistent mode " + re);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 65cb5f4..0dfaf6a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -244,8 +244,8 @@
/**
* Called after virtual display Id is updated by
- * {@link com.android.server.vr.CompatibilityDisplay} with a specific
- * {@param compatibilityDisplayId}.
+ * {@link com.android.server.vr.Vr2dDisplay} with a specific
+ * {@param vr2dDisplayId}.
*/
- public abstract void setVrCompatibilityDisplayId(int vrCompatibilityDisplayId);
+ public abstract void setVr2dDisplayId(int vr2dDisplayId);
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 3221c5d..620e5cf 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -226,11 +226,7 @@
@Override
public void doAlarm(IAlarmCompleteListener alarmManager) {
mCompletion = alarmManager;
- mHandler.post(this);
- }
- @Override
- public void run() {
// Remove this listener from the wrapper cache first; the server side
// already considers it gone
synchronized (AlarmManager.class) {
@@ -239,6 +235,11 @@
}
}
+ mHandler.post(this);
+ }
+
+ @Override
+ public void run() {
// Now deliver it to the app
try {
mListener.onAlarm();
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index f369955..4994fbb 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -39,8 +39,11 @@
* Called whenever IActivityManager.startActivity is called on an activity that is already
* running in the pinned stack and the activity is not actually started, but the task is either
* brought to the front or a new Intent is delivered to it.
+ *
+ * @param clearedTask whether or not the launch activity also cleared the task as a part of
+ * starting
*/
- void onPinnedActivityRestartAttempt();
+ void onPinnedActivityRestartAttempt(boolean clearedTask);
/**
* Called whenever the pinned stack is starting animating a resize.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c8b8c6c..2e56bcf 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4358,6 +4358,8 @@
mN.mLargeIcon = null;
Bitmap largeIconLegacy = mN.largeIcon;
mN.largeIcon = null;
+ ArrayList<Action> actions = mActions;
+ mActions = new ArrayList<>();
Bundle publicExtras = new Bundle();
publicExtras.putBoolean(EXTRA_SHOW_WHEN,
savedBundle.getBoolean(EXTRA_SHOW_WHEN));
@@ -4373,6 +4375,7 @@
mN.extras = savedBundle;
mN.mLargeIcon = largeIcon;
mN.largeIcon = largeIconLegacy;
+ mActions = actions;
mStyle = style;
return view;
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 0265ea5..10d6a7b 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -671,22 +671,22 @@
}
/**
- * Make this service run in the foreground, supplying the ongoing
+ * If your service is started (running through {@link Context#startService(Intent)}), then
+ * also make this service run in the foreground, supplying the ongoing
* notification to be shown to the user while in this state.
- * By default services are background, meaning that if the system needs to
- * kill them to reclaim more memory (such as to display a large page in a
- * web browser), they can be killed without too much harm. You can set this
- * flag if killing your service would be disruptive to the user, such as
+ * By default started services are background, meaning that their process won't be given
+ * foreground CPU scheduling (unless something else in that process is foreground) and,
+ * if the system needs to kill them to reclaim more memory (such as to display a large page in a
+ * web browser), they can be killed without too much harm. You use
+ * {@link #startForeground} if killing your service would be disruptive to the user, such as
* if your service is performing background music playback, so the user
* would notice if their music stopped playing.
- *
- * <p>If you need your application to run on platform versions prior to API
- * level 5, you can use the following model to call the the older setForeground()
- * or this modern method as appropriate:
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
- * foreground_compatibility}
- *
+ *
+ * <p>Note that calling this method does <em>not</em> put the service in the started state
+ * itself, even though the name sounds like it. You must always call
+ * {@link #startService(Intent)} first to tell the system it should keep the service running,
+ * and then use this method to tell it to keep it running harder.</p>
+ *
* @param id The identifier for this notification as per
* {@link NotificationManager#notify(int, Notification)
* NotificationManager.notify(int, Notification)}; must not be 0.
@@ -716,7 +716,9 @@
/**
* Remove this service from foreground state, allowing it to be killed if
- * more memory is needed.
+ * more memory is needed. This does not stop the service from running (for that
+ * you use {@link #stopSelf()} or related methods), just takes it out of the
+ * foreground state.
*
* @param flags additional behavior options.
* @see #startForeground(int, Notification)
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 527314c..60b52d2 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -22,6 +22,7 @@
import android.app.admin.IDevicePolicyManager;
import android.app.job.IJobScheduler;
import android.app.job.JobScheduler;
+import android.app.timezone.RulesManager;
import android.app.trust.TrustManager;
import android.app.usage.IStorageStatsManager;
import android.app.usage.IUsageStatsManager;
@@ -95,7 +96,6 @@
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
-import android.os.Debug;
import android.os.DropBoxManager;
import android.os.HardwarePropertiesManager;
import android.os.IBatteryPropertiesRegistrar;
@@ -118,9 +118,6 @@
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
-import android.telephony.euicc.EuiccManager;
-import android.view.autofill.AutofillManager;
-import android.view.autofill.IAutoFillManager;
import android.service.oemlock.IOemLockService;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.IPersistentDataBlockService;
@@ -130,6 +127,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccManager;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -137,6 +135,8 @@
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -872,6 +872,13 @@
return new VrManager(IVrManager.Stub.asInterface(b));
}
});
+
+ registerService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, RulesManager.class,
+ new CachedServiceFetcher<RulesManager>() {
+ @Override
+ public RulesManager createService(ContextImpl ctx) {
+ return new RulesManager(ctx.getOuterContext());
+ }});
}
/**
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 307fc91..2df011f 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -39,7 +39,7 @@
}
@Override
- public void onPinnedActivityRestartAttempt() throws RemoteException {
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException {
}
@Override
diff --git a/core/java/android/app/CompatibilityDisplayProperties.aidl b/core/java/android/app/Vr2dDisplayProperties.aidl
similarity index 93%
rename from core/java/android/app/CompatibilityDisplayProperties.aidl
rename to core/java/android/app/Vr2dDisplayProperties.aidl
index 626a63e..1e04943 100644
--- a/core/java/android/app/CompatibilityDisplayProperties.aidl
+++ b/core/java/android/app/Vr2dDisplayProperties.aidl
@@ -17,4 +17,4 @@
package android.app;
/** @hide */
-parcelable CompatibilityDisplayProperties;
+parcelable Vr2dDisplayProperties;
diff --git a/core/java/android/app/CompatibilityDisplayProperties.java b/core/java/android/app/Vr2dDisplayProperties.java
similarity index 75%
rename from core/java/android/app/CompatibilityDisplayProperties.java
rename to core/java/android/app/Vr2dDisplayProperties.java
index 9a9bc2c..a608bb0 100644
--- a/core/java/android/app/CompatibilityDisplayProperties.java
+++ b/core/java/android/app/Vr2dDisplayProperties.java
@@ -27,7 +27,7 @@
*
* @hide
*/
-public class CompatibilityDisplayProperties implements Parcelable {
+public class Vr2dDisplayProperties implements Parcelable {
/**
* The actual width, height and dpi.
@@ -36,7 +36,7 @@
private final int mHeight;
private final int mDpi;
- public CompatibilityDisplayProperties(int width, int height, int dpi) {
+ public Vr2dDisplayProperties(int width, int height, int dpi) {
mWidth = width;
mHeight = height;
mDpi = dpi;
@@ -52,7 +52,7 @@
@Override
public String toString() {
- return "CompatibilityDisplayProperties{" +
+ return "Vr2dDisplayProperties{" +
"mWidth=" + mWidth +
", mHeight=" + mHeight +
", mDpi=" + mDpi +
@@ -64,7 +64,7 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- CompatibilityDisplayProperties that = (CompatibilityDisplayProperties) o;
+ Vr2dDisplayProperties that = (Vr2dDisplayProperties) o;
if (getWidth() != that.getWidth()) return false;
if (getHeight() != that.getHeight()) return false;
@@ -83,27 +83,27 @@
dest.writeInt(mDpi);
}
- public static final Parcelable.Creator<CompatibilityDisplayProperties> CREATOR
- = new Parcelable.Creator<CompatibilityDisplayProperties>() {
+ public static final Parcelable.Creator<Vr2dDisplayProperties> CREATOR
+ = new Parcelable.Creator<Vr2dDisplayProperties>() {
@Override
- public CompatibilityDisplayProperties createFromParcel(Parcel source) {
- return new CompatibilityDisplayProperties(source);
+ public Vr2dDisplayProperties createFromParcel(Parcel source) {
+ return new Vr2dDisplayProperties(source);
}
@Override
- public CompatibilityDisplayProperties[] newArray(int size) {
- return new CompatibilityDisplayProperties[size];
+ public Vr2dDisplayProperties[] newArray(int size) {
+ return new Vr2dDisplayProperties[size];
}
};
- private CompatibilityDisplayProperties(Parcel source) {
+ private Vr2dDisplayProperties(Parcel source) {
mWidth = source.readInt();
mHeight = source.readInt();
mDpi = source.readInt();
}
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "CompatibilityDisplayProperties:");
+ pw.println(prefix + "Vr2dDisplayProperties:");
pw.println(prefix + " width=" + mWidth);
pw.println(prefix + " height=" + mHeight);
pw.println(prefix + " dpi=" + mDpi);
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 878c8c3..040b330 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -45,20 +45,20 @@
}
/**
- * Sets the resolution and DPI of the compatibility virtual display used to display 2D
+ * Sets the resolution and DPI of the vr2d virtual display used to display 2D
* applications in VR mode.
*
* <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p>
*
- * @param {@link android.app.CompatibilityDisplayProperties} properties to be set to the
- * virtual display for 2D applications in VR mode.
+ * @param vr2dDisplayProp properties to be set to the virtual display for
+ * 2D applications in VR mode.
*
* {@hide}
*/
- public void setCompatibilityDisplayProperties(
- CompatibilityDisplayProperties compatDisplayProp) {
+ public void setVr2dDisplayProperties(
+ Vr2dDisplayProperties vr2dDisplayProp) {
try {
- mService.setCompatibilityDisplayProperties(compatDisplayProp);
+ mService.setVr2dDisplayProperties(vr2dDisplayProp);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 3353530..7261dfa 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1559,14 +1559,14 @@
@Override
public void setText(CharSequence text) {
ViewNodeText t = getNodeText();
- t.mText = text;
+ t.mText = TextUtils.trimNoCopySpans(text);
t.mTextSelectionStart = t.mTextSelectionEnd = -1;
}
@Override
public void setText(CharSequence text, int selectionStart, int selectionEnd) {
ViewNodeText t = getNodeText();
- t.mText = text;
+ t.mText = TextUtils.trimNoCopySpans(text);
t.mTextSelectionStart = selectionStart;
t.mTextSelectionEnd = selectionEnd;
}
@@ -1737,13 +1737,6 @@
}
@Override
- public void setUrl(String url) {
- if (url == null) return;
-
- setWebDomain(url);
- }
-
- @Override
public void setWebDomain(@Nullable String domain) {
if (domain == null) {
mNode.mWebDomain = null;
diff --git a/core/java/android/app/timezone/Callback.java b/core/java/android/app/timezone/Callback.java
new file mode 100644
index 0000000..b51e5ba
--- /dev/null
+++ b/core/java/android/app/timezone/Callback.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Callback interface for receiving information about an async time zone operation.
+ * The methods will be called on your application's main thread.
+ *
+ * @hide
+ */
+// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+public abstract class Callback {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_INSTALL_BAD_DISTRO_STRUCTURE,
+ ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION, ERROR_INSTALL_RULES_TOO_OLD,
+ ERROR_INSTALL_VALIDATION_ERROR})
+ public @interface AsyncResultCode {}
+
+ /**
+ * Indicates that an operation succeeded.
+ */
+ public static final int SUCCESS = 0;
+
+ /**
+ * Indicates an install / uninstall did not fully succeed for an unknown reason.
+ */
+ public static final int ERROR_UNKNOWN_FAILURE = 1;
+
+ /**
+ * Indicates an install failed because of a structural issue with the provided distro,
+ * e.g. it wasn't in the right format or the contents were structured incorrectly.
+ */
+ public static final int ERROR_INSTALL_BAD_DISTRO_STRUCTURE = 2;
+
+ /**
+ * Indicates an install failed because of a versioning issue with the provided distro,
+ * e.g. it was created for a different version of Android.
+ */
+ public static final int ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION = 3;
+
+ /**
+ * Indicates an install failed because the rules provided are too old for the device,
+ * e.g. the Android device shipped with a newer rules version.
+ */
+ public static final int ERROR_INSTALL_RULES_TOO_OLD = 4;
+
+ /**
+ * Indicates an install failed because the distro contents failed validation.
+ */
+ public static final int ERROR_INSTALL_VALIDATION_ERROR = 5;
+
+ /**
+ * Reports the result of an async time zone operation.
+ */
+ public abstract void onFinished(@AsyncResultCode int status);
+}
diff --git a/core/java/android/app/timezone/DistroFormatVersion.java b/core/java/android/app/timezone/DistroFormatVersion.java
new file mode 100644
index 0000000..e879e8f
--- /dev/null
+++ b/core/java/android/app/timezone/DistroFormatVersion.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Versioning information about a distro's format or a device's supported format.
+ *
+ * <p>The following properties are included:
+ * <dl>
+ * <dt>majorVersion</dt>
+ * <dd>the major distro format version. Major versions differences are not compatible - e.g.
+ * 2 is not compatible with 1 or 3.</dd>
+ * <dt>minorVersion</dt>
+ * <dd>the minor distro format version. Minor versions should be backwards compatible iff the
+ * major versions match exactly, i.e. version 2.2 will be compatible with 2.1 devices but not
+ * 2.3 devices.</dd>
+ * </dl>
+ *
+ * @hide
+ */
+// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+public final class DistroFormatVersion implements Parcelable {
+
+ private final int mMajorVersion;
+ private final int mMinorVersion;
+
+ public DistroFormatVersion(int majorVersion, int minorVersion) {
+ mMajorVersion = Utils.validateVersion("major", majorVersion);
+ mMinorVersion = Utils.validateVersion("minor", minorVersion);
+ }
+
+ public static final Creator<DistroFormatVersion> CREATOR = new Creator<DistroFormatVersion>() {
+ public DistroFormatVersion createFromParcel(Parcel in) {
+ int majorVersion = in.readInt();
+ int minorVersion = in.readInt();
+ return new DistroFormatVersion(majorVersion, minorVersion);
+ }
+
+ public DistroFormatVersion[] newArray(int size) {
+ return new DistroFormatVersion[size];
+ }
+ };
+
+ public int getMajorVersion() {
+ return mMajorVersion;
+ }
+
+ public int getMinorVersion() {
+ return mMinorVersion;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mMajorVersion);
+ out.writeInt(mMinorVersion);
+ }
+
+ /**
+ * If this object describes a device's supported version and the parameter describes a distro's
+ * version, this method returns whether the device would accept the distro.
+ */
+ public boolean supports(DistroFormatVersion distroFormatVersion) {
+ return mMajorVersion == distroFormatVersion.mMajorVersion
+ && mMinorVersion <= distroFormatVersion.mMinorVersion;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DistroFormatVersion that = (DistroFormatVersion) o;
+
+ if (mMajorVersion != that.mMajorVersion) {
+ return false;
+ }
+ return mMinorVersion == that.mMinorVersion;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mMajorVersion;
+ result = 31 * result + mMinorVersion;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "DistroFormatVersion{"
+ + "mMajorVersion=" + mMajorVersion
+ + ", mMinorVersion=" + mMinorVersion
+ + '}';
+ }
+}
diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java
new file mode 100644
index 0000000..5503ce1
--- /dev/null
+++ b/core/java/android/app/timezone/DistroRulesVersion.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import static android.app.timezone.Utils.validateRulesVersion;
+import static android.app.timezone.Utils.validateVersion;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Versioning information about a set of time zone rules.
+ *
+ * <p>The following properties are included:
+ * <dl>
+ * <dt>rulesVersion</dt>
+ * <dd>the IANA rules version. e.g. "2017a"</dd>
+ * <dt>revision</dt>
+ * <dd>the revision for the rules. Allows there to be several revisions for a given IANA rules
+ * release. Numerically higher is newer.</dd>
+ * </dl>
+ *
+ * @hide
+ */
+// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+public final class DistroRulesVersion implements Parcelable {
+
+ private final String mRulesVersion;
+ private final int mRevision;
+
+ public DistroRulesVersion(String rulesVersion, int revision) {
+ mRulesVersion = validateRulesVersion("rulesVersion", rulesVersion);
+ mRevision = validateVersion("revision", revision);
+ }
+
+ public static final Creator<DistroRulesVersion> CREATOR = new Creator<DistroRulesVersion>() {
+ public DistroRulesVersion createFromParcel(Parcel in) {
+ String rulesVersion = in.readString();
+ int revision = in.readInt();
+ return new DistroRulesVersion(rulesVersion, revision);
+ }
+
+ public DistroRulesVersion[] newArray(int size) {
+ return new DistroRulesVersion[size];
+ }
+ };
+
+ public String getRulesVersion() {
+ return mRulesVersion;
+ }
+
+ public int getRevision() {
+ return mRevision;
+ }
+
+ /**
+ * Returns true if this DistroRulesVersion is older than the one supplied. It returns false if
+ * it is the same or newer. This method compares the {@code rulesVersion} and the
+ * {@code revision}.
+ */
+ public boolean isOlderThan(DistroRulesVersion distroRulesVersion) {
+ int rulesComparison = mRulesVersion.compareTo(distroRulesVersion.mRulesVersion);
+ if (rulesComparison < 0) {
+ return true;
+ }
+ if (rulesComparison > 0) {
+ return false;
+ }
+ return mRevision < distroRulesVersion.mRevision;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mRulesVersion);
+ out.writeInt(mRevision);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DistroRulesVersion that = (DistroRulesVersion) o;
+
+ if (mRevision != that.mRevision) {
+ return false;
+ }
+ return mRulesVersion.equals(that.mRulesVersion);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mRulesVersion.hashCode();
+ result = 31 * result + mRevision;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "DistroRulesVersion{"
+ + "mRulesVersion='" + mRulesVersion + '\''
+ + ", mRevision='" + mRevision + '\''
+ + '}';
+ }
+}
diff --git a/core/java/android/app/CompatibilityDisplayProperties.aidl b/core/java/android/app/timezone/ICallback.aidl
similarity index 70%
copy from core/java/android/app/CompatibilityDisplayProperties.aidl
copy to core/java/android/app/timezone/ICallback.aidl
index 626a63e..519ef1a 100644
--- a/core/java/android/app/CompatibilityDisplayProperties.aidl
+++ b/core/java/android/app/timezone/ICallback.aidl
@@ -14,7 +14,14 @@
* limitations under the License.
*/
-package android.app;
+package android.app.timezone;
-/** @hide */
-parcelable CompatibilityDisplayProperties;
+/**
+ * Callback interface for a timezone updater to receive information about the success or failure of
+ * an installation/uninstallation attempt.
+ *
+ * {@hide}
+ */
+oneway interface ICallback {
+ void onFinished(int error);
+}
\ No newline at end of file
diff --git a/core/java/android/app/timezone/IRulesManager.aidl b/core/java/android/app/timezone/IRulesManager.aidl
new file mode 100644
index 0000000..40f3fd2
--- /dev/null
+++ b/core/java/android/app/timezone/IRulesManager.aidl
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import android.app.timezone.ICallback;
+import android.app.timezone.RulesState;
+import android.os.ParcelFileDescriptor;
+
+ /**
+ * Interface to the TimeZone Rules Manager Service.
+ *
+ * <p>This interface is only intended for system apps to call. They should use the
+ * {@link android.app.timezone.RulesManager} class rather than going through this
+ * Binder interface directly. See {@link android.app.timezone.RulesManager} for more complete
+ * documentation.
+ *
+ * {@hide}
+ */
+interface IRulesManager {
+
+ /**
+ * Returns information about the current time zone rules state such as the IANA version of
+ * the system and any currently installed distro. This method is intended to allow clients to
+ * determine if the current state can be improved; for example by passing the information to a
+ * server that may provide a new distro for download.
+ */
+ RulesState getRulesState();
+
+ /**
+ * Requests installation of the supplied distro. The distro must have been checked for integrity
+ * by the caller or have been received via a trusted mechanism.
+ *
+ * @param distroFileDescriptor the file descriptor for the distro
+ * @param checkToken an optional token provided if the install was triggered in response to a
+ * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent
+ * @param callback the {@link ICallback} to receive callbacks related to the
+ * installation
+ * @return zero if the installation will be attempted; nonzero on error
+ */
+ int requestInstall(in ParcelFileDescriptor distroFileDescriptor, in byte[] checkToken,
+ ICallback callback);
+
+ /**
+ * Requests uninstallation of the currently installed distro (leaving the device with no
+ * distro installed).
+ *
+ * @param checkToken an optional token provided if the uninstall was triggered in response to a
+ * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent
+ * @param callback the {@link ICallback} to receive callbacks related to the
+ * uninstall
+ * @return zero if the uninstallation will be attempted; nonzero on error
+ */
+ int requestUninstall(in byte[] checkToken, ICallback callback);
+
+ /**
+ * Requests the system does not modify the currently installed time zone distro, if any. This
+ * method records the fact that a time zone check operation triggered by the system is now
+ * complete and there was nothing to do. The token passed should be the one presented when the
+ * check was triggered.
+ *
+ * <p>Note: Passing {@code success == false} may result in more checks being triggered. Clients
+ * should be careful not to pass false if the failure is unlikely to resolve by itself.
+ *
+ * @param checkToken an optional token provided if the install was triggered in response to a
+ * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent
+ * @param success true if the check was successful, false if it was not successful but may
+ * succeed if it is retried
+ */
+ void requestNothing(in byte[] token, boolean success);
+}
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
new file mode 100644
index 0000000..649d894
--- /dev/null
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * The interface through which a time zone update application interacts with the Android system
+ * to handle time zone rule updates.
+ *
+ * <p>This interface is intended for use with the default APK-based time zone rules update
+ * application but it can also be used by OEMs if that mechanism is turned off using configuration.
+ * All callers must possess the {@link android.Manifest.permission#UPDATE_TIME_ZONE_RULES} system
+ * permission.
+ *
+ * <p>When using the default mechanism, when properly configured the Android system will send a
+ * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent with a
+ * {@link RulesUpdaterContract#EXTRA_CHECK_TOKEN} extra to the time zone rules updater application
+ * when it detects that it or the OEM's APK containing time zone rules data has been modified. The
+ * updater application is then responsible for calling one of
+ * {@link #requestInstall(ParcelFileDescriptor, byte[], Callback)},
+ * {@link #requestUninstall(byte[], Callback)} or
+ * {@link #requestNothing(byte[], boolean)}, indicating, respectively, whether a new time zone rules
+ * distro should be installed, the current distro should be uninstalled, or there is nothing to do
+ * (or that the correct operation could not be determined due to an error). In each case the updater
+ * must pass the {@link RulesUpdaterContract#EXTRA_CHECK_TOKEN} value it received from the intent
+ * back so the system in the {@code checkToken} parameter.
+ *
+ * <p>If OEMs want to handle their own time zone rules updates, perhaps via a server-side component
+ * rather than an APK, then they should disable the default triggering mechanism in config and are
+ * responsible for triggering their own update checks / installs / uninstalls. In this case the
+ * "check token" parameter can be left null and there is never any need to call
+ * {@link #requestNothing(byte[], boolean)}.
+ *
+ * <p>OEMs should not mix the default mechanism and their own as this could lead to conflicts and
+ * unnecessary checks being triggered.
+ *
+ * <p>Applications obtain this using {@link android.app.Activity#getSystemService(String)} with
+ * {@link Context#TIME_ZONE_RULES_MANAGER_SERVICE}.
+ * @hide
+ */
+// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+public final class RulesManager {
+ private static final String TAG = "timezone.RulesManager";
+ private static final boolean DEBUG = false;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_OPERATION_IN_PROGRESS})
+ public @interface ResultCode {}
+
+ /**
+ * Indicates that an operation succeeded.
+ */
+ public static final int SUCCESS = 0;
+
+ /**
+ * Indicates that an install/uninstall cannot be initiated because there is one already in
+ * progress.
+ */
+ public static final int ERROR_OPERATION_IN_PROGRESS = 1;
+
+ /**
+ * Indicates an install / uninstall did not fully succeed for an unknown reason.
+ */
+ public static final int ERROR_UNKNOWN_FAILURE = 2;
+
+ private final Context mContext;
+ private final IRulesManager mIRulesManager;
+
+ public RulesManager(Context context) {
+ mContext = context;
+ mIRulesManager = IRulesManager.Stub.asInterface(
+ ServiceManager.getService(Context.TIME_ZONE_RULES_MANAGER_SERVICE));
+ }
+
+ /**
+ * Returns information about the current time zone rules state such as the IANA version of
+ * the system and any currently installed distro. This method is intended to allow clients to
+ * determine if the current state can be improved; for example by passing the information to a
+ * server that may provide a new distro for download.
+ */
+ public RulesState getRulesState() {
+ try {
+ logDebug("sIRulesManager.getRulesState()");
+ RulesState rulesState = mIRulesManager.getRulesState();
+ logDebug("sIRulesManager.getRulesState() returned " + rulesState);
+ return rulesState;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests installation of the supplied distro. The distro must have been checked for integrity
+ * by the caller or have been received via a trusted mechanism.
+ *
+ * @param distroFileDescriptor the file descriptor for the distro
+ * @param checkToken an optional token provided if the install was triggered in response to a
+ * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent
+ * @param callback the {@link Callback} to receive callbacks related to the installation
+ * @return {@link #SUCCESS} if the installation will be attempted
+ */
+ @ResultCode
+ public int requestInstall(
+ ParcelFileDescriptor distroFileDescriptor, byte[] checkToken, Callback callback)
+ throws IOException {
+
+ ICallback iCallback = new CallbackWrapper(mContext, callback);
+ try {
+ logDebug("sIRulesManager.requestInstall()");
+ return mIRulesManager.requestInstall(distroFileDescriptor, checkToken, iCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests uninstallation of the currently installed distro (leaving the device with no
+ * distro installed).
+ *
+ * @param checkToken an optional token provided if the uninstall was triggered in response to a
+ * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent
+ * @param callback the {@link Callback} to receive callbacks related to the uninstall
+ * @return {@link #SUCCESS} if the uninstallation will be attempted
+ */
+ @ResultCode
+ public int requestUninstall(byte[] checkToken, Callback callback) {
+ ICallback iCallback = new CallbackWrapper(mContext, callback);
+ try {
+ logDebug("sIRulesManager.requestUninstall()");
+ return mIRulesManager.requestUninstall(checkToken, iCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /*
+ * We wrap incoming binder calls with a private class implementation that
+ * redirects them into main-thread actions. This serializes the backup
+ * progress callbacks nicely within the usual main-thread lifecycle pattern.
+ */
+ private class CallbackWrapper extends ICallback.Stub {
+ final Handler mHandler;
+ final Callback mCallback;
+
+ CallbackWrapper(Context context, Callback callback) {
+ mCallback = callback;
+ mHandler = new Handler(context.getMainLooper());
+ }
+
+ // Binder calls into this object just enqueue on the main-thread handler
+ @Override
+ public void onFinished(int status) {
+ logDebug("mCallback.onFinished(status), status=" + status);
+ mHandler.post(() -> mCallback.onFinished(status));
+ }
+ }
+
+ /**
+ * Requests the system does not modify the currently installed time zone distro, if any. This
+ * method records the fact that a time zone check operation triggered by the system is now
+ * complete and there was nothing to do. The token passed should be the one presented when the
+ * check was triggered.
+ *
+ * <p>Note: Passing {@code success == false} may result in more checks being triggered. Clients
+ * should be careful not to pass false if the failure is unlikely to resolve by itself.
+ *
+ * @param checkToken an optional token provided if the install was triggered in response to a
+ * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent
+ * @param succeeded true if the check was successful, false if it was not successful but may
+ * succeed if it is retried
+ */
+ public void requestNothing(byte[] checkToken, boolean succeeded) {
+ try {
+ logDebug("sIRulesManager.requestNothing() with token=" + Arrays.toString(checkToken));
+ mIRulesManager.requestNothing(checkToken, succeeded);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ static void logDebug(String msg) {
+ if (DEBUG) {
+ Log.v(TAG, msg);
+ }
+ }
+}
diff --git a/core/java/android/app/CompatibilityDisplayProperties.aidl b/core/java/android/app/timezone/RulesState.aidl
similarity index 88%
copy from core/java/android/app/CompatibilityDisplayProperties.aidl
copy to core/java/android/app/timezone/RulesState.aidl
index 626a63e..f789120 100644
--- a/core/java/android/app/CompatibilityDisplayProperties.aidl
+++ b/core/java/android/app/timezone/RulesState.aidl
@@ -14,7 +14,4 @@
* limitations under the License.
*/
-package android.app;
-
-/** @hide */
-parcelable CompatibilityDisplayProperties;
+parcelable RulesState;
\ No newline at end of file
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
new file mode 100644
index 0000000..33f4e80
--- /dev/null
+++ b/core/java/android/app/timezone/RulesState.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import static android.app.timezone.Utils.validateConditionalNull;
+import static android.app.timezone.Utils.validateNotNull;
+import static android.app.timezone.Utils.validateRulesVersion;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Description of the state of time zone rules on a device.
+ *
+ * <p>The following properties are included:
+ * <dl>
+ * <dt>systemRulesVersion</dt>
+ * <dd>the IANA rules version that shipped with the OS. Always present. e.g. "2017a".</dd>
+ * <dt>distroFormatVersionSupported</dt>
+ * <dd>the distro format version supported by this device. Always present.</dd>
+ * <dt>operationInProgress</dt>
+ * <dd>{@code true} if there is an install / uninstall operation currently happening.</dd>
+ * <dt>stagedOperationType</dt>
+ * <dd>one of {@link #STAGED_OPERATION_UNKNOWN}, {@link #STAGED_OPERATION_NONE},
+ * {@link #STAGED_OPERATION_UNINSTALL} and {@link #STAGED_OPERATION_INSTALL} indicating whether
+ * there is a currently staged time zone distro operation. {@link #STAGED_OPERATION_UNKNOWN} is
+ * used when {@link #isOperationInProgress()} is {@code true}. Staged operations currently
+ * require a reboot to become active.</dd>
+ * <dt>stagedDistroRulesVersion</dt>
+ * <dd>[present if distroStagedState == STAGED_STATE_INSTALL], the rules version of the distro
+ * currently staged for installation.</dd>
+ * <dt>distroStatus</dt>
+ * <dd>{@link #DISTRO_STATUS_INSTALLED} if there is a time zone distro installed and active,
+ * {@link #DISTRO_STATUS_NONE} if there is no active installed distro.
+ * {@link #DISTRO_STATUS_UNKNOWN} is used when {@link #isOperationInProgress()} is {@code true}.
+ * </dd>
+ * <dt>installedDistroRulesVersion</dt>
+ * <dd>[present if distroStatus == {@link #DISTRO_STATUS_INSTALLED}], the rules version of the
+ * installed and active distro.</dd>
+ * </dl>
+ *
+ * @hide
+ */
+// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+public final class RulesState implements Parcelable {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ STAGED_OPERATION_UNKNOWN,
+ STAGED_OPERATION_NONE,
+ STAGED_OPERATION_UNINSTALL,
+ STAGED_OPERATION_INSTALL })
+ private @interface StagedOperationType {}
+
+ /** Staged state could not be determined. */
+ public static final int STAGED_OPERATION_UNKNOWN = 0;
+ /** Nothing is staged. */
+ public static final int STAGED_OPERATION_NONE = 1;
+ /** An uninstall is staged. */
+ public static final int STAGED_OPERATION_UNINSTALL = 2;
+ /** An install is staged. */
+ public static final int STAGED_OPERATION_INSTALL = 3;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DISTRO_STATUS_UNKNOWN,
+ DISTRO_STATUS_NONE,
+ DISTRO_STATUS_INSTALLED })
+ private @interface DistroStatus {}
+
+ /** The current distro status could not be determined. */
+ public static final int DISTRO_STATUS_UNKNOWN = 0;
+ /** There is no active installed time zone distro. */
+ public static final int DISTRO_STATUS_NONE = 1;
+ /** The is an active, installed time zone distro. */
+ public static final int DISTRO_STATUS_INSTALLED = 2;
+
+ private static final byte BYTE_FALSE = 0;
+ private static final byte BYTE_TRUE = 1;
+
+ private final String mSystemRulesVersion;
+ private final DistroFormatVersion mDistroFormatVersionSupported;
+ private final boolean mOperationInProgress;
+ @StagedOperationType private final int mStagedOperationType;
+ @Nullable private final DistroRulesVersion mStagedDistroRulesVersion;
+ @DistroStatus private final int mDistroStatus;
+ @Nullable private final DistroRulesVersion mInstalledDistroRulesVersion;
+
+ public RulesState(String systemRulesVersion, DistroFormatVersion distroFormatVersionSupported,
+ boolean operationInProgress,
+ @StagedOperationType int stagedOperationType,
+ @Nullable DistroRulesVersion stagedDistroRulesVersion,
+ @DistroStatus int distroStatus,
+ @Nullable DistroRulesVersion installedDistroRulesVersion) {
+ this.mSystemRulesVersion = validateRulesVersion("systemRulesVersion", systemRulesVersion);
+ this.mDistroFormatVersionSupported =
+ validateNotNull("distroFormatVersionSupported", distroFormatVersionSupported);
+ this.mOperationInProgress = operationInProgress;
+
+ if (operationInProgress && stagedOperationType != STAGED_OPERATION_UNKNOWN) {
+ throw new IllegalArgumentException(
+ "stagedOperationType != STAGED_OPERATION_UNKNOWN");
+ }
+ this.mStagedOperationType = validateStagedOperation(stagedOperationType);
+ this.mStagedDistroRulesVersion = validateConditionalNull(
+ mStagedOperationType == STAGED_OPERATION_INSTALL /* requireNotNull */,
+ "stagedDistroRulesVersion", stagedDistroRulesVersion);
+
+ if (operationInProgress && distroStatus != DISTRO_STATUS_UNKNOWN) {
+ throw new IllegalArgumentException("distroInstalled != DISTRO_STATUS_UNKNOWN");
+ }
+ this.mDistroStatus = validateDistroStatus(distroStatus);
+ this.mInstalledDistroRulesVersion = validateConditionalNull(
+ mDistroStatus == DISTRO_STATUS_INSTALLED/* requireNotNull */,
+ "installedDistroRulesVersion", installedDistroRulesVersion);
+ }
+
+ public String getSystemRulesVersion() {
+ return mSystemRulesVersion;
+ }
+
+ public boolean isOperationInProgress() {
+ return mOperationInProgress;
+ }
+
+ public @StagedOperationType int getStagedOperationType() {
+ return mStagedOperationType;
+ }
+
+ /**
+ * Returns the staged rules version when {@link #getStagedOperationType()} is
+ * {@link #STAGED_OPERATION_INSTALL}.
+ */
+ public @Nullable DistroRulesVersion getStagedDistroRulesVersion() {
+ return mStagedDistroRulesVersion;
+ }
+
+ public @DistroStatus int getDistroStatus() {
+ return mDistroStatus;
+ }
+
+ /**
+ * Returns the installed rules version when {@link #getDistroStatus()} is
+ * {@link #DISTRO_STATUS_INSTALLED}.
+ */
+ public @Nullable DistroRulesVersion getInstalledDistroRulesVersion() {
+ return mInstalledDistroRulesVersion;
+ }
+
+ /**
+ * Returns true if a distro in the specified format is supported on this device.
+ */
+ public boolean isDistroFormatVersionSupported(DistroFormatVersion distroFormatVersion) {
+ return mDistroFormatVersionSupported.supports(distroFormatVersion);
+ }
+
+ /**
+ * Returns true if the distro IANA rules version supplied is newer or the same as the version in
+ * the system image data files.
+ */
+ public boolean isSystemVersionOlderThan(DistroRulesVersion distroRulesVersion) {
+ return mSystemRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) < 0;
+ }
+
+ public boolean isDistroInstalled() {
+ return mDistroStatus == DISTRO_STATUS_INSTALLED;
+ }
+
+ /**
+ * Returns true if the rules version supplied is newer than the one currently installed. If
+ * there is no installed distro this method throws IllegalStateException.
+ */
+ public boolean isInstalledDistroOlderThan(DistroRulesVersion distroRulesVersion) {
+ if (mOperationInProgress) {
+ throw new IllegalStateException("Distro state not known: operation in progress.");
+ }
+ if (!isDistroInstalled()) {
+ throw new IllegalStateException("No distro installed.");
+ }
+ return mInstalledDistroRulesVersion.isOlderThan(distroRulesVersion);
+ }
+
+ public static final Parcelable.Creator<RulesState> CREATOR =
+ new Parcelable.Creator<RulesState>() {
+ public RulesState createFromParcel(Parcel in) {
+ return RulesState.createFromParcel(in);
+ }
+
+ public RulesState[] newArray(int size) {
+ return new RulesState[size];
+ }
+ };
+
+ private static RulesState createFromParcel(Parcel in) {
+ String systemRulesVersion = in.readString();
+ DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null);
+ boolean operationInProgress = in.readByte() == BYTE_TRUE;
+ int distroStagedState = in.readByte();
+ DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null);
+ int installedDistroStatus = in.readByte();
+ DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null);
+ return new RulesState(systemRulesVersion, distroFormatVersionSupported, operationInProgress,
+ distroStagedState, stagedDistroRulesVersion,
+ installedDistroStatus, installedDistroRulesVersion);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mSystemRulesVersion);
+ out.writeParcelable(mDistroFormatVersionSupported, 0);
+ out.writeByte(mOperationInProgress ? BYTE_TRUE : BYTE_FALSE);
+ out.writeByte((byte) mStagedOperationType);
+ out.writeParcelable(mStagedDistroRulesVersion, 0);
+ out.writeByte((byte) mDistroStatus);
+ out.writeParcelable(mInstalledDistroRulesVersion, 0);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RulesState that = (RulesState) o;
+
+ if (mOperationInProgress != that.mOperationInProgress) {
+ return false;
+ }
+ if (mStagedOperationType != that.mStagedOperationType) {
+ return false;
+ }
+ if (mDistroStatus != that.mDistroStatus) {
+ return false;
+ }
+ if (!mSystemRulesVersion.equals(that.mSystemRulesVersion)) {
+ return false;
+ }
+ if (!mDistroFormatVersionSupported.equals(that.mDistroFormatVersionSupported)) {
+ return false;
+ }
+ if (mStagedDistroRulesVersion != null ? !mStagedDistroRulesVersion
+ .equals(that.mStagedDistroRulesVersion) : that.mStagedDistroRulesVersion != null) {
+ return false;
+ }
+ return mInstalledDistroRulesVersion != null ? mInstalledDistroRulesVersion
+ .equals(that.mInstalledDistroRulesVersion)
+ : that.mInstalledDistroRulesVersion == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mSystemRulesVersion.hashCode();
+ result = 31 * result + mDistroFormatVersionSupported.hashCode();
+ result = 31 * result + (mOperationInProgress ? 1 : 0);
+ result = 31 * result + mStagedOperationType;
+ result = 31 * result + (mStagedDistroRulesVersion != null ? mStagedDistroRulesVersion
+ .hashCode()
+ : 0);
+ result = 31 * result + mDistroStatus;
+ result = 31 * result + (mInstalledDistroRulesVersion != null ? mInstalledDistroRulesVersion
+ .hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "RulesState{"
+ + "mSystemRulesVersion='" + mSystemRulesVersion + '\''
+ + ", mDistroFormatVersionSupported=" + mDistroFormatVersionSupported
+ + ", mOperationInProgress=" + mOperationInProgress
+ + ", mStagedOperationType=" + mStagedOperationType
+ + ", mStagedDistroRulesVersion=" + mStagedDistroRulesVersion
+ + ", mDistroStatus=" + mDistroStatus
+ + ", mInstalledDistroRulesVersion=" + mInstalledDistroRulesVersion
+ + '}';
+ }
+
+ private static int validateStagedOperation(int stagedOperationType) {
+ if (stagedOperationType < STAGED_OPERATION_UNKNOWN
+ || stagedOperationType > STAGED_OPERATION_INSTALL) {
+ throw new IllegalArgumentException("Unknown operation type=" + stagedOperationType);
+ }
+ return stagedOperationType;
+ }
+
+ private static int validateDistroStatus(int distroStatus) {
+ if (distroStatus < DISTRO_STATUS_UNKNOWN || distroStatus > DISTRO_STATUS_INSTALLED) {
+ throw new IllegalArgumentException("Unknown distro status=" + distroStatus);
+ }
+ return distroStatus;
+ }
+}
diff --git a/core/java/android/app/timezone/RulesUpdaterContract.java b/core/java/android/app/timezone/RulesUpdaterContract.java
new file mode 100644
index 0000000..4e77818
--- /dev/null
+++ b/core/java/android/app/timezone/RulesUpdaterContract.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Constants related to the contract between the Android system and the privileged time zone updater
+ * application.
+ *
+ * @hide
+ */
+// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+public final class RulesUpdaterContract {
+
+ /**
+ * The system permission possessed by the Android system that allows it to trigger time zone
+ * update checks. The updater should be configured to require this permission when registering
+ * for {@link #ACTION_TRIGGER_RULES_UPDATE_CHECK} intents.
+ */
+ public static final String TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION =
+ android.Manifest.permission.TRIGGER_TIME_ZONE_RULES_CHECK;
+
+ /**
+ * The system permission possessed by the time zone rules updater app that allows it to update
+ * device time zone rules. The Android system requires this permission for calls made to
+ * {@link RulesManager}.
+ */
+ public static final String UPDATE_TIME_ZONE_RULES_PERMISSION =
+ android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
+
+ /**
+ * The action of the intent that the Android system will broadcast. The intent will be targeted
+ * at the configured updater application's package meaning the term "broadcast" only loosely
+ * applies.
+ */
+ public static final String ACTION_TRIGGER_RULES_UPDATE_CHECK =
+ "android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK";
+
+ /**
+ * The extra containing the {@code byte[]} that should be passed to
+ * {@link RulesManager#requestInstall(ParcelFileDescriptor, byte[], Callback)},
+ * {@link RulesManager#requestUninstall(byte[], Callback)} and
+ * {@link RulesManager#requestNothing(byte[], boolean)} methods when the
+ * {@link #ACTION_TRIGGER_RULES_UPDATE_CHECK} intent has been processed.
+ */
+ public static final String EXTRA_CHECK_TOKEN =
+ "android.intent.extra.timezone.CHECK_TOKEN";
+
+ /**
+ * Creates an intent that would trigger a time zone rules update check.
+ */
+ public static Intent createUpdaterIntent(String updaterPackageName) {
+ Intent intent = new Intent(RulesUpdaterContract.ACTION_TRIGGER_RULES_UPDATE_CHECK);
+ intent.setPackage(updaterPackageName);
+ intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ return intent;
+ }
+
+ /**
+ * Broadcasts an {@link #ACTION_TRIGGER_RULES_UPDATE_CHECK} intent with the
+ * {@link #EXTRA_CHECK_TOKEN} that triggers an update check, including the required receiver
+ * permission.
+ */
+ public static void sendBroadcast(Context context, String updaterAppPackageName,
+ byte[] checkTokenBytes) {
+ Intent intent = createUpdaterIntent(updaterAppPackageName);
+ intent.putExtra(EXTRA_CHECK_TOKEN, checkTokenBytes);
+ context.sendBroadcast(intent, RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION);
+ }
+}
diff --git a/core/java/android/app/timezone/Utils.java b/core/java/android/app/timezone/Utils.java
new file mode 100644
index 0000000..8dd3fb7
--- /dev/null
+++ b/core/java/android/app/timezone/Utils.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.timezone;
+
+/**
+ * Shared code for android.app.timezone classes.
+ */
+final class Utils {
+ private Utils() {}
+
+ static int validateVersion(String type, int version) {
+ if (version < 0 || version > 999) {
+ throw new IllegalArgumentException("Invalid " + type + " version=" + version);
+ }
+ return version;
+ }
+
+ static String validateRulesVersion(String type, String rulesVersion) {
+ validateNotNull(type, rulesVersion);
+
+ if (rulesVersion.isEmpty()) {
+ throw new IllegalArgumentException(type + " must not be empty");
+ }
+ return rulesVersion;
+ }
+
+ /** Validates that {@code object} is not null. Always returns {@code object}. */
+ static <T> T validateNotNull(String type, T object) {
+ if (object == null) {
+ throw new NullPointerException(type + " == null");
+ }
+ return object;
+ }
+
+ /**
+ * If {@code requireNotNull} is {@code true} calls {@link #validateNotNull(String, Object)},
+ * and {@link #validateNull(String, Object)} otherwise. Returns {@code object}.
+ */
+ static <T> T validateConditionalNull(boolean requireNotNull, String type, T object) {
+ if (requireNotNull) {
+ return validateNotNull(type, object);
+ } else {
+ return validateNull(type, object);
+ }
+ }
+
+ /** Validates that {@code object} is null. Always returns null. */
+ static <T> T validateNull(String type, T object) {
+ if (object != null) {
+ throw new IllegalArgumentException(type + " != null");
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d1ad8de..c58eaa1 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -136,6 +136,38 @@
*/
public static final int STATE_NOT_PLAYING = 11;
+ /**
+ * We don't have a stored preference for whether or not the given A2DP sink device supports
+ * optional codecs.
+ * @hide */
+ public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
+
+ /**
+ * The given A2DP sink device does not support optional codecs.
+ * @hide */
+ public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
+
+ /**
+ * The given A2DP sink device does support optional codecs.
+ * @hide */
+ public static final int OPTIONAL_CODECS_SUPPORTED = 1;
+
+ /**
+ * We don't have a stored preference for whether optional codecs should be enabled or disabled
+ * for the given A2DP device.
+ * @hide */
+ public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
+
+ /**
+ * Optional codecs should be disabled for the given A2DP device.
+ * @hide */
+ public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
+
+ /**
+ * Optional codecs should be enabled for the given A2DP device.
+ * @hide */
+ public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
+
private Context mContext;
private ServiceListener mServiceListener;
private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
@@ -655,6 +687,88 @@
}
/**
+ * Returns whether this device supports optional codecs.
+ *
+ * @param device The device to check
+ * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
+ * OPTIONAL_CODECS_SUPPORTED.
+ *
+ * @hide
+ */
+ public int supportsOptionalCodecs(BluetoothDevice device) {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.supportsOptionalCodecs(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
+ return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Returns whether this device should have optional codecs enabled.
+ *
+ * @param device The device in question.
+ * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
+ * OPTIONAL_CODECS_PREF_DISABLED.
+ *
+ * @hide
+ */
+ public int getOptionalCodecsEnabled(BluetoothDevice device) {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.getOptionalCodecsEnabled(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return OPTIONAL_CODECS_PREF_UNKNOWN;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
+ return OPTIONAL_CODECS_PREF_UNKNOWN;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Sets a persistent preference for whether a given device should have optional codecs enabled.
+ *
+ * @param device The device to set this preference for.
+ * @param value Whether the optional codecs should be enabled for this device. This should be
+ * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
+ * OPTIONAL_CODECS_PREF_DISABLED.
+ * @hide
+ */
+ public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
+ try {
+ if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN &&
+ value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED &&
+ value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
+ Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
+ return;
+ }
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ mService.setOptionalCodecsEnabled(device, value);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index a775a1f..1b533cb 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -42,4 +42,7 @@
oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig);
oneway void enableOptionalCodecs();
oneway void disableOptionalCodecs();
+ int supportsOptionalCodecs(in BluetoothDevice device);
+ int getOptionalCodecsEnabled(in BluetoothDevice device);
+ oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a8214fa..08c7f18 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -34,7 +34,6 @@
import android.annotation.UserIdInt;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
-import android.app.Notification;
import android.app.VrManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -2660,8 +2659,8 @@
/**
* Similar to {@link #startService(Intent)}, but with an implicit promise that the
- * Service will call {@link android.app.Service#startForeground(int, Notification)
- * startForeground(int, Notification)} once it begins running. The service is given
+ * Service will call {@link android.app.Service#startForeground(int, android.app.Notification)
+ * startForeground(int, android.app.Notification)} once it begins running. The service is given
* an amount of time comparable to the ANR interval to do this, otherwise the system
* will automatically stop the service and declare the app ANR.
*
@@ -2682,7 +2681,7 @@
* or the service can not be found.
*
* @see #stopService
- * @see android.app.Service#startForeground(int, Notification)
+ * @see android.app.Service#startForeground(int, android.app.Notification)
*/
@Nullable
public abstract ComponentName startForegroundService(Intent service);
@@ -2865,6 +2864,7 @@
STORAGE_SERVICE,
STORAGE_STATS_SERVICE,
WALLPAPER_SERVICE,
+ TIME_ZONE_RULES_MANAGER_SERVICE,
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
@@ -3968,6 +3968,15 @@
public static final String VR_SERVICE = "vrmanager";
/**
+ * Use with {@link #getSystemService} to retrieve an
+ * {@link android.app.timezone.ITimeZoneRulesManager}.
+ * @hide
+ *
+ * @see #getSystemService
+ */
+ public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index ee56a18..4e53914 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -44,7 +44,6 @@
public boolean notLaunched;
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
- public boolean blockUninstall;
public boolean instantApp;
public int enabled;
public String lastDisableAppCaller;
@@ -75,7 +74,6 @@
notLaunched = o.notLaunched;
hidden = o.hidden;
suspended = o.suspended;
- blockUninstall = o.blockUninstall;
instantApp = o.instantApp;
enabled = o.enabled;
lastDisableAppCaller = o.lastDisableAppCaller;
@@ -193,9 +191,6 @@
if (suspended != oldState.suspended) {
return false;
}
- if (blockUninstall != oldState.blockUninstall) {
- return false;
- }
if (instantApp != oldState.instantApp) {
return false;
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index bdb278b..4586316 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -34,6 +34,11 @@
InputDevice getInputDevice(int deviceId);
int[] getInputDeviceIds();
+ // Enable/disable input device.
+ boolean isInputDeviceEnabled(int deviceId);
+ void enableInputDevice(int deviceId);
+ void disableInputDevice(int deviceId);
+
// Reports whether the hardware supports the given keys; returns true if successful
boolean hasKeys(int deviceId, int sourceMask, in int[] keyCodes, out boolean[] keyExists);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 631b77d..22b3638 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,8 +16,6 @@
package android.hardware.input;
-import com.android.internal.os.SomeArgs;
-
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -31,10 +29,10 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.Vibrator;
-import android.os.VibrationEffect;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
@@ -46,6 +44,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.os.SomeArgs;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -323,6 +323,62 @@
}
/**
+ * Returns true if an input device is enabled. Should return true for most
+ * situations. Some system apps may disable an input device, for
+ * example to prevent unwanted touch events.
+ *
+ * @param id The input device Id.
+ *
+ * @hide
+ */
+ public boolean isInputDeviceEnabled(int id) {
+ try {
+ return mIm.isInputDeviceEnabled(id);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not check enabled status of input device with id = " + id);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables an InputDevice.
+ * <p>
+ * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * </p>
+ *
+ * @param id The input device Id.
+ *
+ * @hide
+ */
+ public void enableInputDevice(int id) {
+ try {
+ mIm.enableInputDevice(id);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not enable input device with id = " + id);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disables an InputDevice.
+ * <p>
+ * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * </p>
+ *
+ * @param id The input device Id.
+ *
+ * @hide
+ */
+ public void disableInputDevice(int id) {
+ try {
+ mIm.disableInputDevice(id);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not disable input device with id = " + id);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Registers an input device listener to receive notifications about when
* input devices are added, removed or changed.
*
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index c08f41f..e09720d 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -30,4 +30,11 @@
RadioManager.BandConfig getConfiguration();
int getProgramInformation(out RadioManager.ProgramInfo[] infoOut);
+
+ /**
+ * @throws IllegalStateException if tuner was opened without audio
+ */
+ void setMuted(boolean mute);
+
+ boolean isMuted();
}
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index 1156fe8..0dddf3b 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -21,7 +21,6 @@
import android.os.RemoteException;
import android.util.Log;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -65,7 +64,8 @@
Log.e(TAG, "Can't set configuration", e);
return RadioManager.STATUS_BAD_VALUE;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Log.e(TAG, "service died", e);
+ return RadioManager.STATUS_DEAD_OBJECT;
}
}
@@ -78,20 +78,33 @@
config[0] = mTuner.getConfiguration();
return RadioManager.STATUS_OK;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Log.e(TAG, "service died", e);
+ return RadioManager.STATUS_DEAD_OBJECT;
}
}
@Override
public int setMute(boolean mute) {
- // TODO(b/36863239): forward to mTuner
- throw new RuntimeException("Not implemented");
+ try {
+ mTuner.setMuted(mute);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Can't set muted", e);
+ return RadioManager.STATUS_ERROR;
+ } catch (RemoteException e) {
+ Log.e(TAG, "service died", e);
+ return RadioManager.STATUS_DEAD_OBJECT;
+ }
+ return RadioManager.STATUS_OK;
}
@Override
public boolean getMute() {
- // TODO(b/36863239): forward to mTuner
- throw new RuntimeException("Not implemented");
+ try {
+ return mTuner.isMuted();
+ } catch (RemoteException e) {
+ Log.e(TAG, "service died", e);
+ return true;
+ }
}
@Override
@@ -126,7 +139,8 @@
try {
return mTuner.getProgramInformation(info);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Log.e(TAG, "service died", e);
+ return RadioManager.STATUS_DEAD_OBJECT;
}
}
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index 73e52c8..d163a44 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -45,7 +45,7 @@
* Set the active scorer and clear existing scores.
* @param packageName the package name of the new scorer to use.
* @return true if the operation succeeded, or false if the new package is not a valid scorer.
- * @throws SecurityException if the caller is not the system.
+ * @throws SecurityException if the caller is not the system or a network scorer.
*/
boolean setActiveScorer(in String packageName);
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index eeb426a..9f6e45c 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -16,27 +16,20 @@
package android.net;
-import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
-
+import android.Manifest.permission;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
-import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-import java.util.concurrent.CompletableFuture;
/**
* Class that manages communication between network subsystems and a network scorer.
@@ -49,9 +42,9 @@
*
* <p>A network scorer is any application which:
* <ul>
- * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
+ * <li>Declares the {@link permission#SCORE_NETWORKS} permission.
* <li>Include a Service for the {@link #ACTION_RECOMMEND_NETWORKS} action
- * protected by the {@link android.Manifest.permission#BIND_NETWORK_RECOMMENDATION_SERVICE}
+ * protected by the {@link permission#BIND_NETWORK_RECOMMENDATION_SERVICE}
* permission.
* </ul>
*
@@ -319,7 +312,7 @@
*
* @return true if the operation succeeded, or false if the new package is not a valid scorer.
* @throws SecurityException if the caller is not a system process or does not hold the
- * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission
+ * {@link permission#SCORE_NETWORKS} permission
* @hide
*/
@SystemApi
@@ -351,7 +344,7 @@
*
* @return true if the broadcast was sent, or false if there is no active scorer.
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
+ * {@link permission#REQUEST_NETWORK_SCORES} permission.
* @hide
*/
public boolean requestScores(NetworkKey[] networks) throws SecurityException {
@@ -368,7 +361,7 @@
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
+ * {@link permission#REQUEST_NETWORK_SCORES} permission.
* @throws IllegalArgumentException if a score cache is already registered for this type.
* @deprecated equivalent to registering for cache updates with CACHE_FILTER_NONE.
* @hide
@@ -385,7 +378,7 @@
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores
* @param filterType the {@link CacheUpdateFilter} to apply
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
+ * {@link permission#REQUEST_NETWORK_SCORES} permission.
* @throws IllegalArgumentException if a score cache is already registered for this type.
* @hide
*/
@@ -404,7 +397,7 @@
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
+ * {@link permission#REQUEST_NETWORK_SCORES} permission.
* @throws IllegalArgumentException if a score cache is already registered for this type.
* @hide
*/
@@ -417,25 +410,6 @@
}
/**
- * Request a recommendation for which network to connect to.
- *
- * <p>It is not safe to call this method from the main thread.
- *
- * @param request a {@link RecommendationRequest} instance containing additional
- * request details
- * @return a {@link RecommendationResult} instance containing the recommended network
- * to connect to
- * @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
- * @hide
- * @deprecated to be removed.
- */
- public RecommendationResult requestRecommendation(RecommendationRequest request)
- throws SecurityException {
- return null;
- }
-
- /**
* Determine whether the application with the given UID is the enabled scorer.
*
* @param callingUid the UID to check
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 4bad7ab..86fcfc8 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -758,7 +758,7 @@
/**
* O.
*/
- public static final int O = 26;
+ public static final int O = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 447f280..e063855 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -812,9 +812,11 @@
int timeTotal = -1;
int uncryptTime = -1;
int sourceVersion = -1;
- int temperature_start = -1;
- int temperature_end = -1;
- int temperature_max = -1;
+ int temperatureStart = -1;
+ int temperatureEnd = -1;
+ int temperatureMax = -1;
+ int errorCode = -1;
+ int causeCode = -1;
while ((line = in.readLine()) != null) {
// Here is an example of lines in last_install:
@@ -861,11 +863,15 @@
bytesStashedInMiB = (bytesStashedInMiB == -1) ? scaled :
bytesStashedInMiB + scaled;
} else if (line.startsWith("temperature_start")) {
- temperature_start = scaled;
+ temperatureStart = scaled;
} else if (line.startsWith("temperature_end")) {
- temperature_end = scaled;
+ temperatureEnd = scaled;
} else if (line.startsWith("temperature_max")) {
- temperature_max = scaled;
+ temperatureMax = scaled;
+ } else if (line.startsWith("error")) {
+ errorCode = scaled;
+ } else if (line.startsWith("cause")) {
+ causeCode = scaled;
}
}
@@ -885,14 +891,20 @@
if (bytesStashedInMiB != -1) {
MetricsLogger.histogram(context, "ota_stashed_in_MiBs", bytesStashedInMiB);
}
- if (temperature_start != -1) {
- MetricsLogger.histogram(context, "ota_temperature_start", temperature_start);
+ if (temperatureStart != -1) {
+ MetricsLogger.histogram(context, "ota_temperature_start", temperatureStart);
}
- if (temperature_end != -1) {
- MetricsLogger.histogram(context, "ota_temperature_end", temperature_end);
+ if (temperatureEnd != -1) {
+ MetricsLogger.histogram(context, "ota_temperature_end", temperatureEnd);
}
- if (temperature_max != -1) {
- MetricsLogger.histogram(context, "ota_temperature_max", temperature_max);
+ if (temperatureMax != -1) {
+ MetricsLogger.histogram(context, "ota_temperature_max", temperatureMax);
+ }
+ if (errorCode != -1) {
+ MetricsLogger.histogram(context, "ota_non_ab_error_code", errorCode);
+ }
+ if (causeCode != -1) {
+ MetricsLogger.histogram(context, "ota_non_ab_cause_code", causeCode);
}
} catch (IOException e) {
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 1ef3916..8302ece 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -26,17 +26,12 @@
private static final String LOG_TAG = "VintfObject";
/**
- * Slurps all device information (both manifests)
- * and report it.
+ * Slurps all device information (both manifests and both matrices)
+ * and report them.
* If any error in getting one of the manifests, it is not included in
* the list.
*/
- public static String[] report() {
- ArrayList<String> ret = new ArrayList<>();
- put(ret, getDeviceManifest(), "device manifest");
- put(ret, getFrameworkManifest(), "framework manifest");
- return ret.toArray(new String[0]);
- }
+ public static native String[] report();
/**
* Verify that the given metadata for an OTA package is compatible with
@@ -50,15 +45,4 @@
*/
public static native int verify(String[] packageInfo);
- // return null if any error, otherwise XML string.
- private static native String getDeviceManifest();
- private static native String getFrameworkManifest();
-
- private static void put(ArrayList<String> list, String content, String message) {
- if (content == null || content.length() == 0) {
- Log.e(LOG_TAG, "Cannot get;" + message + "; check native logs for details.");
- return;
- }
- list.add(content);
- }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1063a46..4a6d8ce 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5176,13 +5176,6 @@
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
- * bluetooth HCI snoop log configuration
- * @hide
- */
- public static final String BLUETOOTH_HCI_LOG =
- "bluetooth_hci_log";
-
- /**
* @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
*/
@Deprecated
@@ -9003,6 +8996,12 @@
public static final String
BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_";
/** {@hide} */
+ public static final String BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX =
+ "bluetooth_a2dp_supports_optional_codecs_";
+ /** {@hide} */
+ public static final String BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX =
+ "bluetooth_a2dp_optional_codecs_enabled_";
+ /** {@hide} */
public static final String
BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_";
/** {@hide} */
@@ -9269,6 +9268,25 @@
}
/**
+ * Get the key that retrieves a bluetooth a2dp device's ability to support optional codecs.
+ * @hide
+ */
+ public static final String getBluetoothA2dpSupportsOptionalCodecsKey(String address) {
+ return BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX +
+ address.toUpperCase(Locale.ROOT);
+ }
+
+ /**
+ * Get the key that retrieves whether a bluetooth a2dp device should have optional codecs
+ * enabled.
+ * @hide
+ */
+ public static final String getBluetoothA2dpOptionalCodecsEnabledKey(String address) {
+ return BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX +
+ address.toUpperCase(Locale.ROOT);
+ }
+
+ /**
* Get the key that retrieves a bluetooth Input Device's priority.
* @hide
*/
@@ -10419,6 +10437,15 @@
public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate";
/**
+ * Displays toasts when an app posts a notification that does not specify a valid channel.
+ *
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS =
+ "show_notification_channel_warnings";
+
+ /**
* Whether cell is enabled/disabled
* @hide
*/
diff --git a/core/java/android/provider/TimeZoneRulesDataContract.java b/core/java/android/provider/TimeZoneRulesDataContract.java
new file mode 100644
index 0000000..19e914b
--- /dev/null
+++ b/core/java/android/provider/TimeZoneRulesDataContract.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import android.net.Uri;
+
+/**
+ * A set of constants for implementing a time zone data content provider, which is used by the time
+ * zone updater application.
+ *
+ * @hide
+ */
+// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+public final class TimeZoneRulesDataContract {
+
+ private TimeZoneRulesDataContract() {}
+
+ /**
+ * The authority that <em>must</em> be used for the time zone data content provider.
+ * To be accepted by the time zone updater application it <em>must</em> be exposed by the
+ * package specified in the config_timeZoneRulesDataPackage config value.
+ */
+ public static final String AUTHORITY = "com.android.timezone";
+
+ /** A content:// style uri to the authority for the time zone data content provider */
+ private static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * The content:// style URI for determining what type of update is available.
+ *
+ * <p>The URI can be queried using
+ * {@link android.content.ContentProvider#query(Uri, String[], String, String[], String)};
+ * the result will be a cursor with a single row. If the {@link #COLUMN_OPERATION}
+ * column is {@link #OPERATION_INSTALL} then see {@link #DATA_URI} for how to obtain the
+ * binary data.
+ */
+ public static final Uri OPERATION_URI = Uri.withAppendedPath(AUTHORITY_URI, "operation");
+
+ /**
+ * The {@code String} column of the {@link #OPERATION_URI} that provides an int specifying the
+ * type of operation to perform. See {@link #OPERATION_NO_OP}, {@link #OPERATION_UNINSTALL} and
+ * {@link #OPERATION_INSTALL}.
+ */
+ public static final String COLUMN_OPERATION = "operation";
+
+ /**
+ * An operation type used when the time zone rules on device should be left as they are.
+ * This is not expected to be used in normal operation but a safe result in the event of an
+ * error that cannot be recovered from.
+ */
+ public static final String OPERATION_NO_OP = "NOOP";
+
+ /**
+ * An operation type used when the current time zone rules on device should be uninstalled,
+ * returning to the values held in the system partition.
+ */
+ public static final String OPERATION_UNINSTALL = "UNINSTALL";
+
+ /**
+ * An operation type used when the current time zone rules on device should be replaced by
+ * a new set obtained via the {@link android.content.ContentProvider#openFile(Uri, String)}
+ * method.
+ */
+ public static final String OPERATION_INSTALL = "INSTALL";
+
+ /**
+ * The {@code nullable int} column of the {@link #OPERATION_URI} that describes the major
+ * version of the distro to be installed.
+ * Only non-null if {@link #COLUMN_OPERATION} contains {@link #OPERATION_INSTALL}.
+ */
+ public static final String COLUMN_DISTRO_MAJOR_VERSION = "distro_major_version";
+
+ /**
+ * The {@code nullable int} column of the {@link #OPERATION_URI} that describes the minor
+ * version of the distro to be installed.
+ * Only non-null if {@link #COLUMN_OPERATION} contains {@link #OPERATION_INSTALL}.
+ */
+ public static final String COLUMN_DISTRO_MINOR_VERSION = "distro_minor_version";
+
+ /**
+ * The {@code nullable String} column of the {@link #OPERATION_URI} that describes the IANA
+ * rules version of the distro to be installed.
+ * Only non-null if {@link #COLUMN_OPERATION} contains {@link #OPERATION_INSTALL}.
+ */
+ public static final String COLUMN_RULES_VERSION = "rules_version";
+
+ /**
+ * The {@code nullable int} column of the {@link #OPERATION_URI} that describes the revision
+ * number of the distro to be installed.
+ * Only non-null if {@link #COLUMN_OPERATION} contains {@link #OPERATION_INSTALL}.
+ */
+ public static final String COLUMN_REVISION = "revision";
+
+ /**
+ * The content:// style URI for obtaining time zone bundle data.
+ *
+ * <p>Use {@link android.content.ContentProvider#openFile(Uri, String)} with "r" mode.
+ */
+ public static final Uri DATA_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
+}
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index 8b2d0c6..fc8afe9 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -16,7 +16,7 @@
package android.service.vr;
-import android.app.CompatibilityDisplayProperties;
+import android.app.Vr2dDisplayProperties;
import android.service.vr.IVrStateCallbacks;
import android.service.vr.IPersistentVrStateCallbacks;
@@ -68,16 +68,16 @@
void setPersistentVrModeEnabled(in boolean enabled);
/**
- * Sets the resolution and DPI of the compatibility virtual display used to display
+ * Sets the resolution and DPI of the vr2d virtual display used to display
* 2D applications in VR mode.
*
* <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p>
*
- * @param compatDisplayProperties Compatibitlity display properties to be set for
+ * @param vr2dDisplayProperties Vr2d display properties to be set for
* the VR virtual display
*/
- void setCompatibilityDisplayProperties(
- in CompatibilityDisplayProperties compatDisplayProperties);
+ void setVr2dDisplayProperties(
+ in Vr2dDisplayProperties vr2dDisplayProperties);
/**
* Return current virtual display id.
@@ -85,7 +85,7 @@
* @return {@link android.view.Display.INVALID_DISPLAY} if there is no virtual display
* currently, else return the display id of the virtual display
*/
- int getCompatibilityDisplayId();
+ int getVr2dDisplayId();
/**
* Initiate connection for system controller data.
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 081deeb..91f43d6 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1921,6 +1921,22 @@
return false;
}
+ /**
+ * If the {@code charSequence} is instance of {@link Spanned}, creates a new copy and
+ * {@link NoCopySpan}'s are removed from the copy. Otherwise the given {@code charSequence} is
+ * returned as it is.
+ *
+ * @hide
+ */
+ @Nullable
+ public static CharSequence trimNoCopySpans(@Nullable CharSequence charSequence) {
+ if (charSequence != null && charSequence instanceof Spanned) {
+ // SpannableStringBuilder copy constructor trims NoCopySpans.
+ return new SpannableStringBuilder(charSequence);
+ }
+ return charSequence;
+ }
+
private static Object sLock = new Object();
private static char[] sTemp = null;
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index ea2434e..8405d9e 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
@@ -768,6 +770,36 @@
}
/**
+ * Returns true if input device is enabled.
+ * @return Whether the input device is enabled.
+ */
+ public boolean isEnabled() {
+ return InputManager.getInstance().isInputDeviceEnabled(mId);
+ }
+
+ /**
+ * Enables the input device.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE)
+ @TestApi
+ public void enable() {
+ InputManager.getInstance().enableInputDevice(mId);
+ }
+
+ /**
+ * Disables the input device.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE)
+ @TestApi
+ public void disable() {
+ InputManager.getInstance().disableInputDevice(mId);
+ }
+
+ /**
* Reports whether the device has a built-in microphone.
* @return Whether the device has a built-in microphone.
*/
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index f71589c..6bdc9ff 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -374,13 +374,6 @@
public abstract Rect getTempRect();
/**
- * @deprecated - use {@link #setWebDomain(String)} instead.
- * @hide
- */
- @Deprecated
- public abstract void setUrl(String url);
-
- /**
* Sets the Web domain represented by this node.
*
* <p>Typically used when the view is a container for an HTML document.
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index b57dab5..3beae11 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.view.View;
import com.android.internal.util.Preconditions;
@@ -257,7 +258,8 @@
* <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
*/
public static AutofillValue forText(@Nullable CharSequence value) {
- return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT, value);
+ return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT,
+ TextUtils.trimNoCopySpans(value));
}
/**
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index ebf1c01..209ff09 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -607,6 +607,7 @@
@Nullable
public static Intent create(Context context, String type, String text) {
type = type.trim().toLowerCase(Locale.ENGLISH);
+ text = text.trim();
switch (type) {
case TextClassifier.TYPE_EMAIL:
return new Intent(Intent.ACTION_SENDTO)
@@ -618,6 +619,9 @@
return new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse(String.format("geo:0,0?q=%s", text)));
case TextClassifier.TYPE_URL:
+ if (!text.startsWith("https://") && !text.startsWith("http://")) {
+ text = "http://" + text;
+ }
return new Intent(Intent.ACTION_VIEW, Uri.parse(text))
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
default:
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ad3a99d..b0d6395 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1404,6 +1404,11 @@
// or double-clicks that could "dismiss" the floating toolbar.
int delay = ViewConfiguration.getDoubleTapTimeout();
mTextView.postDelayed(mShowFloatingToolbar, delay);
+
+ // This classifies the text and most likely returns before the toolbar is actually
+ // shown. If not, it will update the toolbar with the result when classification
+ // returns. We would rather not wait for a long running classification process.
+ invalidateActionModeAsync();
}
}
@@ -1853,7 +1858,7 @@
mInsertionPointCursorController.invalidateHandle();
}
if (mTextActionMode != null) {
- invalidateActionModeAsync();
+ invalidateActionMode();
}
}
@@ -1945,12 +1950,12 @@
if (mRestartActionModeOnNextRefresh) {
// To avoid distraction, newly start action mode only when selection action
// mode is being restarted.
- startSelectionActionMode();
+ startSelectionActionModeAsync(false);
}
} else if (selectionController == null || !selectionController.isActive()) {
// Insertion action mode is active. Avoid dismissing the selection.
stopTextActionModeWithPreservingSelection();
- startSelectionActionMode();
+ startSelectionActionModeAsync(false);
} else {
mTextActionMode.invalidateContentRect();
}
@@ -2004,15 +2009,8 @@
/**
* Asynchronously starts a selection action mode using the TextClassifier.
*/
- void startSelectionActionModeAsync() {
- getSelectionActionModeHelper().startActionModeAsync();
- }
-
- /**
- * Synchronously starts a selection action mode without the TextClassifier.
- */
- void startSelectionActionMode() {
- getSelectionActionModeHelper().startActionMode();
+ void startSelectionActionModeAsync(boolean adjustSelection) {
+ getSelectionActionModeHelper().startActionModeAsync(adjustSelection);
}
/**
@@ -2022,6 +2020,15 @@
getSelectionActionModeHelper().invalidateActionModeAsync();
}
+ /**
+ * Synchronously invalidates an action mode without the TextClassifier.
+ */
+ private void invalidateActionMode() {
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidate();
+ }
+ }
+
private SelectionActionModeHelper getSelectionActionModeHelper() {
if (mSelectionActionModeHelper == null) {
mSelectionActionModeHelper = new SelectionActionModeHelper(this);
@@ -2075,7 +2082,7 @@
}
if (mTextActionMode != null) {
// Text action mode is already started
- invalidateActionModeAsync();
+ invalidateActionMode();
return false;
}
@@ -4703,7 +4710,7 @@
}
positionAtCursorOffset(offset, false);
if (mTextActionMode != null) {
- invalidateActionModeAsync();
+ invalidateActionMode();
}
}
@@ -4787,7 +4794,7 @@
}
updateDrawable();
if (mTextActionMode != null) {
- invalidateActionModeAsync();
+ invalidateActionMode();
}
}
@@ -5414,13 +5421,8 @@
resetDragAcceleratorState();
if (mTextView.hasSelection()) {
- // Do not invoke the text assistant if this was a drag selection.
- if (mHaventMovedEnoughToStartDrag) {
- startSelectionActionModeAsync();
- } else {
- startSelectionActionMode();
- }
-
+ // Drag selection should not be adjusted by the text classifier.
+ startSelectionActionModeAsync(mHaventMovedEnoughToStartDrag);
}
break;
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 1457d02..569fe01 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -49,7 +49,6 @@
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
-import com.android.internal.util.Preconditions;
import com.google.android.collect.Lists;
@@ -336,11 +335,6 @@
* @param isSelectable whether the item is selectable
*/
public void addHeaderView(View v, Object data, boolean isSelectable) {
- Preconditions.checkState(
- v.getParent() == null,
- "The specified child already has a parent. "
- + "You must call removeView() on the child's parent first.");
-
final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
@@ -435,11 +429,6 @@
* @param isSelectable true if the footer view can be selected
*/
public void addFooterView(View v, Object data, boolean isSelectable) {
- Preconditions.checkState(
- v.getParent() == null,
- "The specified child already has a parent. "
- + "You must call removeView() on the child's parent first.");
-
final FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index c9d172f..16a1087 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -65,7 +65,7 @@
textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
}
- public void startActionModeAsync() {
+ public void startActionModeAsync(boolean adjustSelection) {
cancelAsyncTask();
if (isNoOpTextClassifier() || !hasSelection()) {
// No need to make an async call for a no-op TextClassifier.
@@ -74,16 +74,16 @@
} else {
resetTextClassificationHelper();
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
- mEditor.getTextView(), TIMEOUT_DURATION,
- mTextClassificationHelper::suggestSelection, this::startActionMode)
+ mEditor.getTextView(),
+ TIMEOUT_DURATION,
+ adjustSelection
+ ? mTextClassificationHelper::suggestSelection
+ : mTextClassificationHelper::classifyText,
+ this::startActionMode)
.execute();
}
}
- public void startActionMode() {
- startActionMode(null);
- }
-
public void invalidateActionModeAsync() {
cancelAsyncTask();
if (isNoOpTextClassifier() || !hasSelection()) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 242dcf5..eaf1115 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10579,7 +10579,7 @@
Selection.setSelection((Spannable) text, start, end);
// Make sure selection mode is engaged.
if (mEditor != null) {
- mEditor.startSelectionActionMode();
+ mEditor.startSelectionActionModeAsync(false);
}
return true;
}
diff --git a/core/java/com/android/internal/inputmethod/LocaleUtils.java b/core/java/com/android/internal/inputmethod/LocaleUtils.java
index b18f83c..eeb3854 100644
--- a/core/java/com/android/internal/inputmethod/LocaleUtils.java
+++ b/core/java/com/android/internal/inputmethod/LocaleUtils.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.icu.util.ULocale;
import android.os.LocaleList;
+import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -41,7 +42,7 @@
/**
* Calculates a matching score for the single desired locale.
*
- * @see LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])
+ * @see LocaleUtils#filterByLanguage(List, LocaleExtractor, LocaleList, ArrayList)
*
* @param supported The locale supported by IME subtype.
* @param desired The locale preferred by user.
@@ -72,48 +73,6 @@
return 3;
}
- /**
- * Calculates a matching score for the desired locale list.
- *
- * <p>The supported locale gets a matching score of 3 if all language, script and country of the
- * supported locale matches with the desired locale. The supported locale gets a matching
- * score of 2 if the language and script of the supported locale matches with the desired
- * locale. The supported locale gets a matching score of 1 if only language of the supported
- * locale matches with the desired locale. The supported locale gets a matching score of 0 if
- * the language of the supported locale doesn't match with the desired locale.</p>
- *
- * @param supported The locale supported by IME subtyle.
- * @param desired The locale list preferred by user. Typically system locale list.
- * @param out The output buffer to be stored the individual score for the desired language list.
- * The length of {@code out} must be same as the length of {@code desired} language list.
- * @return {@code false} if supported locale doesn't match with any desired locale list.
- * Otherwise {@code true}.
- */
- private static boolean calculateMatchingScore(@NonNull final ULocale supported,
- @NonNull final LocaleList desired, @NonNull byte[] out) {
- if (desired.isEmpty()) {
- return false;
- }
-
- boolean allZeros = true;
- final int N = desired.size();
- for (int i = 0; i < N; ++i) {
- final Locale locale = desired.get(i);
-
- if (!locale.getLanguage().equals(supported.getLanguage())) {
- // TODO: cache the result of addLikelySubtags if it is slow.
- out[i] = 0;
- } else {
- out[i] = calculateMatchingSubScore(
- supported, ULocale.addLikelySubtags(ULocale.forLocale(locale)));
- if (allZeros && out[i] != 0) {
- allZeros = false;
- }
- }
- }
- return !allZeros;
- }
-
private static final class ScoreEntry implements Comparable<ScoreEntry> {
public int mIndex = -1;
@NonNull public final byte[] mScore; // matching score of the i-th system languages.
@@ -175,17 +134,17 @@
/**
* Filters the given items based on language preferences.
*
- * <p>For each language found in {@code preferredLanguages}, this method tries to copy at most
+ * <p>For each language found in {@code preferredLocales}, this method tries to copy at most
* one best-match item from {@code source} to {@code dest}. For example, if
- * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLanguages},
+ * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLocales},
* this method tries to copy at most one English locale, at most one Japanese, and at most one
* French locale from {@code source} to {@code dest}. Here the best matching English locale
* will be searched from {@code source} based on matching score. For the score design, see
- * {@link LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])}</p>
+ * {@link LocaleUtils#calculateMatchingSubScore(ULocale, ULocale)}</p>
*
* @param sources Source items to be filtered.
* @param extractor Type converter from the source items to {@link Locale} object.
- * @param preferredLanguages Ordered list of locales with which the input items will be
+ * @param preferredLocales Ordered list of locales with which the input items will be
* filtered.
* @param dest Destination into which the filtered items will be added.
* @param <T> Type of the data items.
@@ -194,17 +153,43 @@
public static <T> void filterByLanguage(
@NonNull List<T> sources,
@NonNull LocaleExtractor<T> extractor,
- @NonNull LocaleList preferredLanguages,
+ @NonNull LocaleList preferredLocales,
@NonNull ArrayList<T> dest) {
+ if (preferredLocales.isEmpty()) {
+ return;
+ }
+
+ final int numPreferredLocales = preferredLocales.size();
final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
- final byte[] score = new byte[preferredLanguages.size()];
+ final byte[] score = new byte[numPreferredLocales];
+ final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
final int sourceSize = sources.size();
for (int i = 0; i < sourceSize; ++i) {
final Locale locale = extractor.get(sources.get(i));
- if (locale == null ||
- !calculateMatchingScore(ULocale.addLikelySubtags(ULocale.forLocale(locale)),
- preferredLanguages, score)) {
+ if (locale == null) {
+ continue;
+ }
+
+ boolean canSkip = true;
+ for (int j = 0; j < numPreferredLocales; ++j) {
+ final Locale preferredLocale = preferredLocales.get(j);
+ if (!TextUtils.equals(locale.getLanguage(), preferredLocale.getLanguage())) {
+ score[j] = 0;
+ continue;
+ }
+ if (preferredULocaleCache[j] == null) {
+ preferredULocaleCache[j] = ULocale.addLikelySubtags(
+ ULocale.forLocale(preferredLocale));
+ }
+ score[j] = calculateMatchingSubScore(
+ preferredULocaleCache[j],
+ ULocale.addLikelySubtags(ULocale.forLocale(locale)));
+ if (canSkip && score[j] != 0) {
+ canSkip = false;
+ }
+ }
+ if (canSkip) {
continue;
}
diff --git a/core/jni/android/graphics/Movie.h b/core/jni/android/graphics/Movie.h
index c0fbe4f..736890d 100644
--- a/core/jni/android/graphics/Movie.h
+++ b/core/jni/android/graphics/Movie.h
@@ -10,8 +10,9 @@
#ifndef Movie_DEFINED
#define Movie_DEFINED
-#include "SkRefCnt.h"
+#include "SkBitmap.h"
#include "SkCanvas.h"
+#include "SkRefCnt.h"
class SkStreamRewindable;
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 9491a1e..033f2df 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,10 @@
#define LOG_TAG "VintfObject"
//#define LOG_NDEBUG 0
+#include <android-base/logging.h>
+
+#include <vector>
+#include <string>
#include <JNIHelp.h>
#include <vintf/VintfObject.h>
@@ -23,31 +27,48 @@
#include "core_jni_helpers.h"
+static jclass gString;
+
namespace android {
-using vintf::HalManifest;
-using vintf::RuntimeInfo;
using vintf::VintfObject;
using vintf::gHalManifestConverter;
+using vintf::gCompatibilityMatrixConverter;
+using vintf::XmlConverter;
-static jstring android_os_VintfObject_getDeviceManifest(JNIEnv* env, jclass clazz)
-{
- const HalManifest *manifest = VintfObject::GetDeviceHalManifest();
- if (manifest == nullptr) {
- return nullptr;
+static inline jobjectArray toJavaStringArray(JNIEnv* env, const std::vector<std::string>& v) {
+ jobjectArray ret = env->NewObjectArray(v.size(), gString, NULL /* init element */);
+ for (size_t i = 0; i < v.size(); ++i) {
+ env->SetObjectArrayElement(ret, i, env->NewStringUTF(v[i].c_str()));
}
- std::string xml = gHalManifestConverter(*manifest);
- return env->NewStringUTF(xml.c_str());
+ return ret;
}
-static jstring android_os_VintfObject_getFrameworkManifest(JNIEnv* env, jclass clazz)
-{
- const HalManifest *manifest = VintfObject::GetFrameworkHalManifest();
- if (manifest == nullptr) {
- return nullptr;
+template<typename T>
+static void tryAddSchema(const T* object, const XmlConverter<T>& converter,
+ const std::string& description,
+ std::vector<std::string>* cStrings) {
+ if (object == nullptr) {
+ LOG(WARNING) << __FUNCTION__ << "Cannot get " << description;
+ } else {
+ cStrings->push_back(converter(*object));
}
- std::string xml = gHalManifestConverter(*manifest);
- return env->NewStringUTF(xml.c_str());
+}
+
+static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass clazz)
+{
+ std::vector<std::string> cStrings;
+
+ tryAddSchema(VintfObject::GetDeviceHalManifest(), gHalManifestConverter,
+ "device manifest", &cStrings);
+ tryAddSchema(VintfObject::GetFrameworkHalManifest(), gHalManifestConverter,
+ "framework manifest", &cStrings);
+ tryAddSchema(VintfObject::GetDeviceCompatibilityMatrix(), gCompatibilityMatrixConverter,
+ "device compatibility matrix", &cStrings);
+ tryAddSchema(VintfObject::GetFrameworkCompatibilityMatrix(), gCompatibilityMatrixConverter,
+ "framework compatibility matrix", &cStrings);
+
+ return toJavaStringArray(env, cStrings);
}
static jint android_os_VintfObject_verify(JNIEnv *env, jclass clazz, jobjectArray packageInfo) {
@@ -66,15 +87,18 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod gVintfObjectMethods[] = {
- {"getDeviceManifest", "()Ljava/lang/String;", (void*)android_os_VintfObject_getDeviceManifest},
- {"getFrameworkManifest", "()Ljava/lang/String;", (void*)android_os_VintfObject_getFrameworkManifest},
- {"verify", "([Ljava/lang/String;)I", (void*)android_os_VintfObject_verify},
+ {"report", "()[Ljava/lang/String;", (void*)android_os_VintfObject_report},
+ {"verify", "([Ljava/lang/String;)I", (void*)android_os_VintfObject_verify},
};
+
const char* const kVintfObjectPathName = "android/os/VintfObject";
int register_android_os_VintfObject(JNIEnv* env)
{
+
+ gString = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/String"));
+
return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
NELEM(gVintfObjectMethods));
}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index ce951de..ea40fd5 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -329,6 +329,8 @@
SettingProto max_notification_enqueue_rate = 284;
SettingProto cell_on = 285;
SettingProto network_recommendations_package = 286;
+ SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
+ SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
}
message SecureSettingsProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5e34e05..5f6d820 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2209,6 +2209,22 @@
<permission android:name="android.permission.UPDATE_CONFIG"
android:protectionLevel="signature|privileged" />
+ <!-- Allows a time zone rule updater application to request
+ the system installs / uninstalls timezone rules.
+ <p>An application requesting this permission is responsible for
+ verifying the source and integrity of the update before passing
+ it off to the installer components.
+ @hide -->
+ <permission android:name="android.permission.UPDATE_TIME_ZONE_RULES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a time zone rule updater application,
+ to ensure that only the system can trigger it.
+ @hide -->
+ <permission android:name="android.permission.TRIGGER_TIME_ZONE_RULES_CHECK"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.TRIGGER_TIME_ZONE_RULES_CHECK"/>
+
<!-- Allows the system to reset throttling in shortcut manager.
@hide -->
<permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING"
@@ -2743,6 +2759,13 @@
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
android:protectionLevel="signature" />
+ <!-- Allows an application to disable/enable input devices.
+ Could be used to prevent unwanted touch events
+ on a touchscreen, for example during swimming or rain.
+ @hide -->
+ <permission android:name="android.permission.DISABLE_INPUT_DEVICE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to configure and connect to Wifi displays
@hide -->
<permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 4524b6e..3477a7e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Opletberigte"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Kleinhandeldemonstrasie"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-verbinding"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Programme wat op die agtergrond loop"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans op die agtergrond"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> programme loop tans op die agtergrond"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tik vir besonderhede oor battery- en datagebruik"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Veiligmodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-stelsel"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Skakel oor na persoonlik"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB vir MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Gekoppel aan \'n USB-toebehoorsel"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tik vir meer opsies."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Oudiobykomstigheid word nie gesteun nie"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tik vir meer inligting"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-ontfouter gekoppel"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tik om USB-ontfouting te deaktiveer."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Kies om USB-ontfouting te deaktiveer."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a3ebecb..6d2aa7a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"ማንቂያዎች"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"የችርቻሮ ማሳያ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"የዩኤስቢ ግንኙነት"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"በጀርባ ውስጥ የሚያሄዱ መተግበሪያዎች"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> በጀርባ ውስጥ እያሄደ ነው"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> መተግበሪያዎች በጀርባ ውስጥ እያሄዱ ነው"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"በባትሪ እና ውሂብ አጠቃቀም ላይ ዝርዝሮችን ለማግኘት መታ ያድርጉ"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>፣ <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"የሚያስተማምን ሁነታ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android ስርዓት"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ወደ የግል ቀይር"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"ዩኤስቢ ለMIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"ለUSB ተቀጥላ ተያይዟል"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"ለተጨማሪ አማራጮች መታ ያድርጉ።"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB አድስ ተያይዟል"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"የዩኤስቢ ማረሚያን ለማሰናከል መታ ያድርጉ።"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ማረሚያ ላለማንቃት ምረጥ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d1b9dcb..a080138 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -76,7 +76,7 @@
<string name="ClirMmi" msgid="7784673673446833091">"معرف المتصل الصادر"</string>
<string name="ColpMmi" msgid="3065121483740183974">"معرّف الخط المتصل"</string>
<string name="ColrMmi" msgid="4996540314421889589">"تقييد معرّف الخط المتصل"</string>
- <string name="CfMmi" msgid="5123218989141573515">"اعادة توجيه المكالمة"</string>
+ <string name="CfMmi" msgid="5123218989141573515">"إعادة توجيه المكالمة"</string>
<string name="CwMmi" msgid="9129678056795016867">"انتظار المكالمة"</string>
<string name="BaMmi" msgid="455193067926770581">"حظر الاتصال"</string>
<string name="PwdMmi" msgid="7043715687905254199">"تغيير كلمة المرور"</string>
@@ -92,7 +92,7 @@
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"الإعداد الافتراضي لمعرف المتصل هو غير مقيّد. الاتصال التالي: مقيّد"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"الإعداد الافتراضي لمعرف المتصل هو غير مقيّد. الاتصال التالي: غير مقيّد"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"الخدمة غير متوفرة."</string>
- <string name="CLIRPermanent" msgid="3377371145926835671">"لا يمكنك تغيير إعداد معرف المتصل."</string>
+ <string name="CLIRPermanent" msgid="3377371145926835671">"لا يمكنك تغيير إعداد معرّف المتصل."</string>
<string name="RestrictedOnDataTitle" msgid="1322504692764166532">"ليست هناك خدمة بيانات"</string>
<string name="RestrictedOnEmergencyTitle" msgid="1236071219598685236">"لا تتوفر خدمة طوارئ"</string>
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"لا تتوفر خدمة صوتية"</string>
@@ -104,7 +104,7 @@
<string name="NetworkPreferenceSwitchTitle" msgid="4008877505368566980">"يتعذر الوصول إلى الشبكة"</string>
<string name="NetworkPreferenceSwitchSummary" msgid="4164230263214915351">"لتحسين الاستقبال، يمكنك تجربة تغيير النوع المحدّد في النظام > الشبكة والإنترنت > شبكات الجوّال > نوع الشبكة المفضّل."</string>
<string name="notification_channel_network_alert" msgid="4427736684338074967">"التنبيهات"</string>
- <string name="notification_channel_call_forward" msgid="2419697808481833249">"اعادة توجيه المكالمة"</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_alert" msgid="6130875231721406231">"تنبيهات بيانات الجوّال"</string>
<string name="notification_channel_sms" msgid="3441746047346135073">"الرسائل القصيرة SMS"</string>
@@ -277,16 +277,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"التنبيهات"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"عرض توضيحي لبائع التجزئة"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"اتصال USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"التطبيقات التي تعمل في الخلفية"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"جارٍ تشغيل <xliff:g id="APP_NAME">%1$s</xliff:g> في الخلفية"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"جارٍ تشغيل <xliff:g id="NUMBER">%1$d</xliff:g> تطبيق في الخلفية"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"انقر للحصول على تفاصيل حول البطارية واستخدام البيانات"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>، <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"الوضع الآمن"</string>
<string name="android_system_label" msgid="6577375335728551336">"نظام Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"التبديل إلى الشخصي"</string>
@@ -1263,6 +1258,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB لـ MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"الاتصال بجهاز USB ملحق"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"انقر للحصول على المزيد من الخيارات."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ملحق الصوت غير متوافق"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"انقر للحصول على مزيد من المعلومات"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"تم توصيل تصحيح أخطاء USB"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"انقر لتعطيل تصحيح أخطاء USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"اختيار تعطيل تصحيح أخطاء USB."</string>
@@ -1380,7 +1377,7 @@
<string name="tethered_notification_message" msgid="2113628520792055377">"انقر للإعداد."</string>
<string name="back_button_label" msgid="2300470004503343439">"رجوع"</string>
<string name="next_button_label" msgid="1080555104677992408">"التالي"</string>
- <string name="skip_button_label" msgid="1275362299471631819">"تخطٍ"</string>
+ <string name="skip_button_label" msgid="1275362299471631819">"تخطي"</string>
<string name="no_matches" msgid="8129421908915840737">"ليس هناك أية مطابقات"</string>
<string name="find_on_page" msgid="1946799233822820384">"بحث في الصفحة"</string>
<plurals name="matches_found" formatted="false" msgid="1210884353962081884">
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 552c4e3..72d0bdc 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Siqnallar"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Pərakəndə demo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB əlaqə"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Arxa fonda işləyən tətbiqlər"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> arxa fonda işləyir"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> tətbiq arxa fonda işləyir"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Batareya və data istifadəsi haqqında ətraflı məlumat üçün klikləyin"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Təhlükəsiz rejim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistemi"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Şəxsi profilə keçirin"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI üçün USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB aksesuara qoşuldu"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Əlavə seçimlər üçün tıklayın."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio aksesuar dəstəklənmir"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Daha çox məlumat üçün klikləyin"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB sazlama qoşuludur"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB debaqı deaktivasiya etmək üçün tıklayın."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USb debaqı deaktivasiya etməyi seçin."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 9fc3a28c..6667a414 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -268,16 +268,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Obaveštenja"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Režim demonstracije za maloprodajne objekte"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB veza"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikacije pokrenute u pozadini"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> je pokrenuta u pozadini"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Aplikacije (<xliff:g id="NUMBER">%1$d</xliff:g>) su pokrenute u pozadini"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Dodirnite da biste pregledali detalje o bateriji i potrošnji podataka"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Bezbedni režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistem"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Pređi na Lični profil"</string>
@@ -300,13 +295,13 @@
<string name="permgroupdesc_phone" msgid="6234224354060641055">"upućuje telefonske pozive i upravlja njima"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Senzori za telo"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"pristupa podacima senzora o vitalnim funkcijama"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Preuzima sadržaj prozora"</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"da preuzima sadržaj prozora"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Proverava sadržaj prozora sa kojim ostvarujete interakciju."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Uključi Istraživanja dodirom"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"da uključi Istraživanja dodirom"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"Stavke koje dodirnete će biti izgovorene naglas, a možete da se krećete po ekranu pokretima."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Prati tekst koji unosite"</string>
+ <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"da prati tekst koji unosite"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obuhvata lične podatke kao što su brojevi kreditnih kartica i lozinke."</string>
- <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Upravljaj uvećanjem prikaza"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"da upravlja uvećanjem prikaza"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Upravlja nivoom zumiranja prikaza i određivanjem položaja."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Obavljanje pokreta"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Može da dodiruje, lista, skuplja prikaz i obavlja druge pokrete."</string>
@@ -1053,7 +1048,7 @@
<string name="noApplications" msgid="2991814273936504689">"Nijedna aplikacija ne može da obavlja ovu radnju."</string>
<string name="aerr_application" msgid="250320989337856518">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> je zaustavljena"</string>
<string name="aerr_process" msgid="6201597323218674729">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> je zaustavljen"</string>
- <string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> se stalno zaustavlja"</string>
+ <string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> se stalno zaustavlja(ju)"</string>
<string name="aerr_process_repeated" msgid="6235302956890402259">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> se stalno zaustavlja"</string>
<string name="aerr_restart" msgid="7581308074153624475">"Ponovo otvori aplikaciju"</string>
<string name="aerr_report" msgid="5371800241488400617">"Pošaljite povratne informacije"</string>
@@ -1203,6 +1198,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Povezano sa USB dodatkom"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za još opcija."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Dodatna oprema za audio sadržaj nije podržana"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Dodirnite za više informacija"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka sa USB-a je uspostavljeno"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da biste onemogućili otklanjanje grešaka sa USB-a."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Izaberite da biste onemogućili otklanjanja grešaka sa USB-a."</string>
@@ -1312,7 +1309,7 @@
<string name="vpn_lockdown_config" msgid="5099330695245008680">"Dodirnite da biste podesili"</string>
<string name="upload_file" msgid="2897957172366730416">"Odaberi datoteku"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nije izabrana nijedna datoteka"</string>
- <string name="reset" msgid="2448168080964209908">"Ponovo postavi"</string>
+ <string name="reset" msgid="2448168080964209908">"Resetuj"</string>
<string name="submit" msgid="1602335572089911941">"Pošalji"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Režim rada u automobilu je omogućen"</string>
<string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Dodirnite da biste izašli iz režima rada u automobilu."</string>
@@ -1346,7 +1343,7 @@
<string name="sync_really_delete" msgid="2572600103122596243">"Izbriši stavke"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Opozovi brisanja"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Ne radi ništa za sada"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"Izbor naloga"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"Izaberite nalog"</string>
<string name="add_account_label" msgid="2935267344849993553">"Dodaj nalog"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"Dodaj nalog"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Povećavanje"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index fdd15d3..c564168 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Абвесткi"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Дэманстрацыйны рэжым для пунктаў продажу"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Падключэнне USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Праграмы, якія працуюць у фонавым рэжыме"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> працуе ў фонавым рэжыме"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Праграмы (<xliff:g id="NUMBER">%1$d</xliff:g>) працуюць у фонавым рэжыме"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Дакраніцеся, каб атрымаць падрабязныя звесткі пра выкарыстанне трафіка і акумулятара"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Бяспечны рэжым"</string>
<string name="android_system_label" msgid="6577375335728551336">"Сістэма Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Пераключыцца на асабісты"</string>
@@ -1223,6 +1218,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB для MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Падключаны да USB-прылады"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Дакраніцеся, каб атрымаць іншыя параметры."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аксесуар аўдыя не падтрымліваецца"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Націсніце, каб атрымаць дадатковую інфармацыю"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Адладка па USB падключана"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Дакраніцеся, каб адключыць адладку па USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Выберыце, каб адключыць адладку USB."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index b03e09e..8c78183 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Сигнали"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Демонстрационен режим за магазини"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB връзка"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Приложения, работещи на заден план"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> работи на заден план"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> приложения работят на заден план"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Докоснете за подробности относно използването на батерията и преноса на данни"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасен режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Превключване към личния потребителски профил"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Установена е връзка с аксесоар за USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Докоснете за още опции."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Отстраняване на грешки през USB"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Докоснете, за да деактивирате отстраняването на грешки през USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изберете, за да деактивирате отстраняването на грешки през USB."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b574890..4eaa927 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"সতর্কতাগুলি"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"খুচরা বিক্রয়ের ডেমো"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB সংযোগ"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"পটভূমিতে অ্যাপ চালু আছে"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"পটভূমিতে <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপ চালু আছে"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"পটভূমিতে <xliff:g id="NUMBER">%1$d</xliff:g>টি অ্যাপ চালু আছে"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ব্যাটারি এবং ডেটার ব্যবহারের বিশদ বিবরণের জন্য ট্যাপ করুন"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"নিরাপদ মোড"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android সিস্টেম"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ব্যক্তিগততে পাল্টান"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI এর জন্য USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"একটি USB যন্ত্রাংশতে সংযুক্ত হয়েছে"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"আরো বিকল্পের জন্য আলতো চাপুন৷"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ডিবাগিং সংযুক্ত হয়েছে"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ডিবাগিং অক্ষম করতে আলতো চাপুন৷"</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 37fa55f..e811f71 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -268,16 +268,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Upozorenja"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Promotivna demonstracija u maloprodaji"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB veza"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikacije koje rade u pozadini"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> radi u pozadini"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Broj aplikacija koje rade u pozadini: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Dodirnite za detalje o bateriji i prijenosu podataka"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistem"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Prebacite se na lični"</string>
@@ -1207,6 +1202,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Uspostavljena veza sa USB pohranom"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za više opcija."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio pribor nije podržan"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Dodirnite za više informacija"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka putem uređaja spojenog na USB je uspostavljeno"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da onemogućite otklanjanje grešaka putem uređaja spojenog na USB."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
@@ -1317,7 +1314,7 @@
<string name="vpn_lockdown_config" msgid="5099330695245008680">"Dodirnite za postavke"</string>
<string name="upload_file" msgid="2897957172366730416">"Odabir fajla"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nije izabran nijedan fajl"</string>
- <string name="reset" msgid="2448168080964209908">"Vrati na zadano"</string>
+ <string name="reset" msgid="2448168080964209908">"Vraćanje na zadano"</string>
<string name="submit" msgid="1602335572089911941">"Potvrdi"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Način rada u autu omogućen"</string>
<string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Dodirnite za izlaz iz načina rada u automobilu"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 0b77c82..a3e9fc9 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertes"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demostració comercial"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Connexió USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplicacions que s\'estan executant en segon pla"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant en segon pla"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicacions s\'estan executant en segon pla"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Toca per obtenir informació sobre l\'ús de dades i la bateria"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode segur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Canvia al perfil personal"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per a MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connectat a un accessori USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Toca per veure més opcions."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuració USB activada"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Toca per desactivar la depuració USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecciona per desactivar la depuració USB"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 31f0826..e02eaec 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Upozornění"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Prodejní ukázka"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Připojení USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikace běžící na pozadí"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> běží na pozadí"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Aplikace (<xliff:g id="NUMBER">%1$d</xliff:g>) běží na pozadí"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Nouzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Přepnout na osobní profil"</string>
@@ -1223,6 +1218,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB v režimu MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Připojeno k perifernímu zařízení USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Klepnutím zobrazíte další možnosti."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Zvukové příslušenství není podporováno"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Klepnutím zobrazíte další informace"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ladění přes USB připojeno"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Klepnutím zakážete ladění USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vyberte, chcete-li zakázat ladění USB."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index a258f08..b1df60b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Underretninger"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo til udstilling i butik"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-forbindelse"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps, der kører i baggrunden"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører i baggrunden"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps kører i baggrunden"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tryk for at se oplysninger om batteri- og dataforbrug"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Skift til Tilpasset"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB til MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Tilsluttet et USB-ekstraudstyr"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tryk for at se flere muligheder."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-fejlretning er tilsluttet"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tryk for at deaktivere fejlretning via USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vælg for at deaktivere USB-fejlretning."</string>
@@ -1618,7 +1617,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"Installeret af din administrator"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"Opdateret af din administrator"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"Slettet af din administrator"</string>
- <string name="battery_saver_description" msgid="1960431123816253034">"Batterisparefunktionen hjælper med at forlænge batteriets levetid ved at reducere enhedens ydeevne og begrænse vibration, placeringstjenester og det meste baggrundsdata. E-mail, beskedfunktioner og andre apps, der benytter synkronisering, opdateres muligvis ikke, medmindre du åbner dem.\n\nBatterisparefunktionen slukker automatisk, når enheden oplader."</string>
+ <string name="battery_saver_description" msgid="1960431123816253034">"Batterisparefunktionen hjælper med at forlænge batteriets levetid ved at reducere enhedens ydeevne og begrænse vibration, placeringstjenester og det meste baggrundsdata. E-mail, beskedfunktioner og andre apps, der benytter synkronisering, opdateres muligvis ikke, medmindre du åbner dem.\n\nBatterisparefunktionen deaktiveres automatisk, når enheden oplader."</string>
<string name="data_saver_description" msgid="6015391409098303235">"Datasparefunktionen forhindrer nogle apps i at sende eller modtage data i baggrunden for at reducere dataforbruget. En app, der er i brug, kan få adgang til data, men gør det måske ikke så ofte. Dette kan f.eks. betyde, at billeder ikke vises, før du trykker på dem."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"Vil du slå Datasparefunktion til?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"Slå til"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 5a70d9b..d7810a2 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Warnmeldungen"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo für Einzelhandel"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-Verbindung"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps, die im Hintergrund ausgeführt werden"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird im Hintergrund ausgeführt"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> Apps werden im Hintergrund ausgeführt"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Für weitere Informationen zur Akku- und Datennutzung tippen"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Abgesicherter Modus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-System"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Zu \"Privat\" wechseln"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB für MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Mit USB-Zubehör verbunden"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Für weitere Optionen tippen."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-Debugging aktiviert"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Zum Deaktivieren von USB-Debugging tippen."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB-Debugging deaktivieren: auswählen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 0cc6dc6..cda02d2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Ειδοποιήσεις"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Επίδειξη λιανικής"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Σύνδεση USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Εφαρμογές που εκτελούνται στο παρασκήνιο"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται στο παρασκήνιο"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> εφαρμογές εκτελούνται στο παρασκήνιο"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Πατήστε για λεπτομέρειες σχετικά με τη χρήση μπαταρίας και δεδομένων"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Ασφαλής λειτουργία"</string>
<string name="android_system_label" msgid="6577375335728551336">"Σύστημα Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Μετάβαση σε προσωπικό προφίλ"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB για MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Σύνδεση σε αξεσουάρ USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Πατήστε για περισσότερες επιλογές."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Το εξάρτημα ήχου δεν υποστηρίζεται"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Πατήστε για να δείτε περισσότερες πληροφορίες"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Συνδέθηκε ο εντοπισμός σφαλμάτων USB"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Πατήστε για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 4976b03..d1d474b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alerts"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Retail demo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB connection"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps running in background"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> is running in the background"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are running in the background"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tap for details on battery and data usage"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Switch to Personal"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio accessory not supported"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap for more info"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tap to disable USB debugging."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4976b03..d1d474b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alerts"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Retail demo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB connection"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps running in background"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> is running in the background"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are running in the background"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tap for details on battery and data usage"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Switch to Personal"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio accessory not supported"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap for more info"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tap to disable USB debugging."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 4976b03..d1d474b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alerts"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Retail demo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB connection"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps running in background"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> is running in the background"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are running in the background"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tap for details on battery and data usage"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Switch to Personal"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio accessory not supported"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap for more info"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tap to disable USB debugging."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 5515ae1..d95cdb6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo para punto de venta"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Conexión USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps que se ejecutan en segundo plano"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando en segundo plano"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps se están ejecutando en segundo plano"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Presiona para obtener información sobre el uso de datos y de la batería"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Cambiar al perfil personal"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Presiona para ver más opciones."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"El accesorio de audio no es compatible"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Presiona para obtener más información"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración por USB conectada"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Presiona para inhabilitar la depuración por USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccionar para desactivar la depuración por USB"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 33c0e5c..a986c21 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo para tiendas"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Conexión USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplicaciones que se están ejecutando en segundo plano"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando en segundo plano"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicaciones se están ejecutando en segundo plano"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Toca para ver información detallada sobre el uso de datos y la batería"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Cambiar a perfil personal"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Toca para ver más opciones."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accesorio de audio no compatible"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toca para obtener más información"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB habilitada"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Toca para inhabilitar la depuración USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccionar para inhabilitar la depuración USB"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 1c76b3f..c426454 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Teatised"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Poedemo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-ühendus"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Rakendusi käitatakse taustal"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> käitatakse taustal"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> rakendust käitatakse taustal"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Aku ja andmekasutuse üksikasjade nägemiseks puudutage"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Turvarežiim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-süsteem"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Lülita isiklikule profiilile"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI jaoks"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ühendatud USB-lisaseadmega"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Puudutage lisavalikute nägemiseks."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-silumine ühendatud"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Puudutage USB-silumise keelamiseks."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Valige USB silumise keelamiseks"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 78aed23..c4585de 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Abisuak"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Saltzaileentzako demoa"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB konexioa"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikazioak exekutatzen ari dira atzeko planoan"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> exekutatzen ari da atzeko planoan"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> exekutatzen ari dira atzeko planoan"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Sakatu bateriaren eta datuen erabilerari buruzko xehetasunak ikusteko"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modu segurua"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistema"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Aldatu profil pertsonalera"</string>
@@ -304,7 +299,7 @@
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Behatu idazten duzun testua"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ez da salbuespenik egiten datu pertsonalekin, hala nola, kreditu-txartelen zenbakiekin eta pasahitzekin."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolatu pantailaren zoom-maila"</string>
- <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolatu pantailaren zoom-maila eta kokapena."</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolatu pantailaren zoom-maila eta posizioa."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Keinuak egin"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Sakatu, lerratu, atximurkatu eta beste hainbat keinu egin ditzake."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Hatz-marken keinuak"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI modurako USBa"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB osagarri batera konektatuta"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Sakatu aukera gehiago ikusteko."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Ez da onartzen audio-osagarri hori"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Informazio gehiago lortzeko, sakatu hau"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB arazketa konektatuta"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Sakatu USB arazketa desgaitzeko."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index cd8e750..b12951a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"هشدارها"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"نمونه برای خردهفروشان"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"اتصال USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"برنامههایی که در پسزمینه اجرا میشوند"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> در پسزمینه درحال اجرا شدن است"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> برنامه در پسزمینه درحال اجرا شدن هستند"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"برای جزئیات مربوط به مصرف باتری و داده، ضربه بزنید"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>، <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"حالت ایمن"</string>
<string name="android_system_label" msgid="6577375335728551336">"سیستم Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"رفتن به نمایه شخصی"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB برای MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"به یک وسیله جانبی USB وصل شده است"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"برای گزینههای بیشتر ضربه بزنید."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"اشکالزدایی USB متصل شد"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"برای غیرفعال کردن اشکالزدایی USB ضربه بزنید."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"انتخاب کنید تا رفع عیب USB غیرفعال شود."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 5690669..4d1b075 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Ilmoitukset"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Esittelytila"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-yhteys"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Sovelluksia käynnissä taustalla"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä taustalla."</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> sovellusta on käynnissä taustalla."</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Katso lisätietoja akun ja datan käytöstä napauttamalla."</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Siirry henkilökohtaiseen profiiliin"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB on MIDI-tilassa"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Liitetty USB-laitteeseen"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Näet lisää vaihtoehtoja napauttamalla."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Äänilisälaitetta ei tueta."</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Saat lisätietoja napauttamalla."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-vianetsintä yhdistetty"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Poista USB-vianetsintä käytöstä napauttamalla."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Poista USB-vianetsintä käytöstä valitsemalla tämä."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 7797274..b067fb9 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertes"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Démo en magasin"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Connexion USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Applications qui fonctionnent en arrière-plan"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> fonctionne en arrière-plan"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> applications fonctionnent en arrière-plan"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Touchez ici pour afficher des détails sur l\'usage de la pile et des données"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Passer au profil personnel"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB pour MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connecté à un accessoire USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Touchez pour afficher plus d\'options."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB connecté"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Touchez pour désactiver le débogage USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Sélectionnez cette option pour désactiver le débogage USB."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 761947e..ba33460 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertes"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Démonstration en magasin"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Connexion USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Applications en cours d\'exécution en arrière-plan"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'exécute en arrière-plan"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> applications s\'exécutent en arrière-plan"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Appuyer pour obtenir des informations sur l\'utilisation de la batterie et des données"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Passer au profil personnel"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB en mode MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connecté à un accessoire USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Appuyez ici pour plus d\'options."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB activé"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Appuyez ici pour désactiver le débogage USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Sélectionnez cette option pour désactiver le débogage USB."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 2c58acb..0a5ba2c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demostración comercial"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"conexión USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplicacións que se executan en segundo plano"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Estase executando en segundo plano a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Estanse executando en segundo plano <xliff:g id="NUMBER">%1$d</xliff:g> aplicacións"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Toca para obter información sobre a batería e o uso de datos"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Cambiar ao perfil persoal"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Toca para ver máis opcións."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB conectada"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Toca para desactivar a depuración de erros de USB."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 77f7212e..54c493d1 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"ચેતવણીઓ"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"રિટેલ ડેમો"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB કનેક્શન"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"પૃષ્ઠભૂમિમાં ચાલી રહેલ ઍપ્લિકેશનો"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> પૃષ્ઠભૂમિમાં ચાલી રહી છે"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ઍપ્લિકેશન પૃષ્ઠભૂમિમાં ચાલી રહી છે"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"બૅટરી અને ડેટા વપરાશ વિશેની વિગતો માટે ટૅપ કરો"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"સુરક્ષિત મોડ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android સિસ્ટમ"</string>
<string name="user_owner_label" msgid="1119010402169916617">"વ્યક્તિગત પર સ્વિચ કરો"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI માટે USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ઍક્સેસરીથી કનેક્ટ થયાં"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"વધુ વિકલ્પો માટે ટૅપ કરો."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ડીબગિંગ કનેક્ટ થયું."</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ડીબગિંગ અક્ષમ કરવા માટે ટૅપ કરો."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 0a672ca..74a7a04 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"सूचनाएं"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"खुदरा डेमो"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB कनेक्शन"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"ऐप्लिकेशन बैकग्राउंड में चल रहे हैं"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> बैकग्राउंड में चल रहा है"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ऐप्लिकेशन बैकग्राउंड में चल रहे हैं"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"बैटरी और डेटा उपयोग के विवरण पाने के लिए टैप करें"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android सिस्टम"</string>
<string name="user_owner_label" msgid="1119010402169916617">"व्यक्तिगत प्रोफ़ाइल में स्विच करें"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI के लिए USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB सहायक सामग्री से कनेक्ट किया गया"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"अधिक विकल्पों के लिए टैप करें."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ऑडियो एक्सेसरी इस पर काम नहीं करती है"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"अधिक जानकारी के लिए टैप करें"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग कनेक्ट किया गया"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB डीबग करना अक्षम करने के लिए टैप करें."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डीबग करना अक्षम करने के लिए चुनें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 48f4cf7..3a11fe7 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -268,16 +268,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Upozorenja"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Prodajni demo-način"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB veza"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Izvođenje aplikacija u pozadini"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> izvodi se u pozadini"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Aplikacije koje se izvode u pozadini: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Dodirnite da biste vidjeli pojedinosti o potrošnji baterije i podataka"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sustav Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Prijeđite na osobni"</string>
@@ -1203,6 +1198,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Spojen na USB pribor"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za više opcija."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audiododatak nije podržan"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Dodirnite za više informacija"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Priključen je alat za otklanjanje pogrešaka USB-om"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da biste onemogućili otklanjanje pogrešaka putem USB-a."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Odaberite da biste onemogućili rješavanje programske pogreške na USB-u."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 8af3ff5..6da0f11 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Értesítések"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Kiskereskedelmi bemutató"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-kapcsolat"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"A háttérben még futnak alkalmazások"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás még fut a háttérben"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> alkalmazás még fut a háttérben"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Az akkumulátor töltöttségének és az adathasználat részleteinek megtekintéséhez koppintson"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Biztonsági üzemmód"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android rendszer"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Átváltás személyes profilra"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI-hez"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Csatlakoztatva egy USB-kiegészítőhöz"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Koppintson a további beállítások megjelenítéséhez."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Ez az audiotartozék nem támogatott"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Koppintson, ha további információra van szüksége"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB hibakereső csatlakoztatva"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Koppintson az USB fejlesztő mód kikapcsolásához."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Válassza ezt az USB hibakeresés kikapcsolásához."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 2ca708d..e2523a6 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Ծանուցումներ"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Խանութի ցուցադրական ռեժիմ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB կապակցում"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Հետին ֆոնին գործարկվող հավելվածներ"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը գործարկվում է հետին ֆոնին"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> հավելվածները գործարկվում են հետին ֆոնին"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Հպել՝ մարտկոցի և տվյալների օգտագործման մասին մանրամասներ ստանալու համար"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Անվտանգ ռեժիմ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android համակարգ"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Անցնել անհատական պրոֆիլին"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-ի USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Կապակցված է USB լրասարքի"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Հպեք՝ լրացուցիչ ընտրանքների համար:"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB վրիպազերծումը միացված է"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Հպեք՝ USB վրիպազերծումն անջատելու համար:"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Ընտրել` USB կարգաբերումը կասեցնելու համար:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index aa325ac..f50a384 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -231,12 +231,12 @@
<string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Laporan interaktif"</string>
- <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Gunakan ini di berbagai keadaan. Ini memungkinkan Anda melacak kemajuan laporan, memasukkan detail masalah selengkapnya, dan mengambil tangkapan layar. Mungkin menghilangkan beberapa bagian yang jarang digunakan dan yang perlu waktu lama untuk dilaporkan."</string>
+ <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Gunakan ini di berbagai keadaan. Ini memungkinkan Anda melacak kemajuan laporan, memasukkan detail masalah selengkapnya, dan mengambil screenshot. Mungkin menghilangkan beberapa bagian yang jarang digunakan dan yang perlu waktu lama untuk dilaporkan."</string>
<string name="bugreport_option_full_title" msgid="6354382025840076439">"Laporan lengkap"</string>
- <string name="bugreport_option_full_summary" msgid="7210859858969115745">"Gunakan opsi ini untuk meminimalkan gangguan sistem jika perangkat tidak responsif atau terlalu lambat, atau jika Anda perlu semua bagian laporan. Tidak mengizinkan Anda memasukkan lebih banyak detail atau mengambil tangkapan layar tambahan."</string>
+ <string name="bugreport_option_full_summary" msgid="7210859858969115745">"Gunakan opsi ini untuk meminimalkan gangguan sistem jika perangkat tidak responsif atau terlalu lambat, atau jika Anda perlu semua bagian laporan. Tidak mengizinkan Anda memasukkan lebih banyak detail atau mengambil screenshot tambahan."</string>
<plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
- <item quantity="other">Mengambil tangkapan layar untuk laporan bug dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik.</item>
- <item quantity="one">Mengambil tangkapan layar untuk laporan bug dalam <xliff:g id="NUMBER_0">%d</xliff:g> detik.</item>
+ <item quantity="other">Mengambil screenshot untuk laporan bug dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik.</item>
+ <item quantity="one">Mengambil screenshot untuk laporan bug dalam <xliff:g id="NUMBER_0">%d</xliff:g> detik.</item>
</plurals>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Mode senyap"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Suara MATI"</string>
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Notifikasi"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo promo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Sambungan USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikasi yang sedang berjalan di latar belakang"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan di latar belakang"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikasi sedang berjalan di latar belakang"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tap untuk melihat detail baterai dan penggunaan data"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode aman"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Beralih ke Pribadi"</string>
@@ -820,7 +815,7 @@
<string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nYakin ingin beranjak dari laman ini?"</string>
<string name="save_password_label" msgid="6860261758665825069">"Konfirmasi"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Kiat: Ketuk dua kali untuk memperbesar dan memperkecil."</string>
- <string name="autofill_this_form" msgid="4616758841157816676">"Isiotomatis"</string>
+ <string name="autofill_this_form" msgid="4616758841157816676">"IsiOtomatis"</string>
<string name="setup_autofill" msgid="7103495070180590814">"Siapkan Pengisian Otomatis"</string>
<string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
<string name="autofill_address_summary_name_format" msgid="3268041054899214945">"$1$2$3"</string>
@@ -981,7 +976,7 @@
<string name="selectTextMode" msgid="1018691815143165326">"Pilih teks"</string>
<string name="undo" msgid="7905788502491742328">"Urungkan"</string>
<string name="redo" msgid="7759464876566803888">"Ulangi"</string>
- <string name="autofill" msgid="3035779615680565188">"Isiotomatis"</string>
+ <string name="autofill" msgid="3035779615680565188">"IsiOtomatis"</string>
<string name="textSelectionCABTitle" msgid="5236850394370820357">"Pemilihan teks"</string>
<string name="addToDictionary" msgid="4352161534510057874">"Tambahkan ke kamus"</string>
<string name="deleteText" msgid="6979668428458199034">"Hapus"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB untuk MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Tersambung ke aksesori USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Ketuk untuk opsi lainnya."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Aksesori audio tidak didukung"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap untuk info selengkapnya"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Debugging USB terhubung"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Ketuk untuk menonaktifkan debug USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pilih untuk menonaktifkan debugging USB."</string>
@@ -1618,7 +1615,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"Diinstal oleh admin Anda"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"Diupdate oleh admin Anda"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"Dihapus oleh admin Anda"</string>
- <string name="battery_saver_description" msgid="1960431123816253034">"Untuk membantu meningkatkan masa pakai baterai, penghemat baterai mengurangi kinerja perangkat dan membatasi getaran, layanan lokasi, dan kebanyakan data latar belakang. Email, perpesanan, dan aplikasi lain yang mengandalkan sinkronisasi mungkin tidak diperbarui kecuali jika dibuka.\n\nPenghemat baterai otomatis nonaktif jika perangkat diisi dayanya."</string>
+ <string name="battery_saver_description" msgid="1960431123816253034">"Untuk membantu meningkatkan masa pakai baterai, penghemat baterai mengurangi kinerja perangkat dan membatasi getaran, layanan lokasi, dan sebagian besar data latar belakang. Email, pesan, dan aplikasi lain yang mengandalkan sinkronisasi mungkin tidak diperbarui kecuali jika dibuka.\n\nPenghemat baterai otomatis nonaktif jika perangkat diisi dayanya."</string>
<string name="data_saver_description" msgid="6015391409098303235">"Untuk membantu mengurangi penggunaan data, Penghemat Data mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah disentuh."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"Aktifkan Penghemat Data?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"Aktifkan"</string>
@@ -1746,8 +1743,8 @@
<string name="time_picker_prompt_label" msgid="7588093983899966783">"Ketik waktu"</string>
<string name="time_picker_text_input_mode_description" msgid="4148166758173708199">"Beralih ke mode masukan teks untuk masukan waktu."</string>
<string name="time_picker_radial_mode_description" msgid="4953403779779557198">"Beralih ke mode jam untuk masukan waktu."</string>
- <string name="autofill_picker_accessibility_title" msgid="8469043291648711535">"Opsi Isiotomatis"</string>
- <string name="autofill_save_accessibility_title" msgid="7244365268417107822">"Simpan untuk Isiotomatis"</string>
+ <string name="autofill_picker_accessibility_title" msgid="8469043291648711535">"Opsi IsiOtomatis"</string>
+ <string name="autofill_save_accessibility_title" msgid="7244365268417107822">"Simpan untuk IsiOtomatis"</string>
<string name="autofill_error_cannot_autofill" msgid="7402758580060110371">"Konten tidak dapat diisi otomatis"</string>
<string name="autofill_save_title" msgid="3345527308992082601">"Simpan ke <b><xliff:g id="LABEL">%1$s</xliff:g></b>?"</string>
<string name="autofill_save_title_with_type" msgid="8637809388029313305">"Simpan <xliff:g id="TYPE">%1$s</xliff:g> ke <b><xliff:g id="LABEL">%2$s</xliff:g></b>?"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index e37862b..281faf3 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Tilkynningar"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Kynningarútgáfa fyrir verslanir"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-tenging"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Forrit sem keyra í bakgrunni"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> keyrir í bakgrunni"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> forrit keyra í bakgrunni"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Ýttu til að fá upplýsingar um rafhlöðu- og gagnanotkun"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Örugg stilling"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android kerfið"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Skipta yfir í persónulegt snið"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB fyrir MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Tengt við USB-aukabúnað"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Ýttu til að sjá fleiri valkosti."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Hljóðaukabúnaður er ekki studdur"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Ýttu til að fá frekari upplýsingar"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-villuleit tengd"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Ýttu til að slökkva á USB-villuleit."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 8057a06..9541111 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Avvisi"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo retail"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Connessione USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"App in esecuzione in background"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> è in esecuzione in background"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> app sono in esecuzione in background"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tocca per conoscere i dettagli sull\'utilizzo dei dati e della batteria"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modalità provvisoria"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Passa al profilo personale"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per la modalità MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Collegato a un accessorio USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tocca per altre opzioni."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accessorio audio non supportato"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tocca per ulteriori informazioni"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Debug USB collegato"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tocca per disattivare il debug USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleziona per disattivare il debug USB."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 504d3ad..a087430 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"התראות"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"הדגמה לקמעונאים"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"חיבור USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"אפליקציות שפועלות ברקע"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> פועלת ברקע"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> אפליקציות פועלות ברקע"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"הקש לקבלת פרטים על צריכה של נתונים וסוללה"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="RIGHT_SIDE">%2$s</xliff:g>, <xliff:g id="LEFT_SIDE">%1$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"מצב בטוח"</string>
<string name="android_system_label" msgid="6577375335728551336">"מערכת Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"עבור ל\'אישי\'"</string>
@@ -1223,6 +1218,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB ל-MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"מחובר לאביזר USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"הקש לקבלת אפשרויות נוספות."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"ניפוי באגים של USB מחובר"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"הקש כדי להשבית ניפוי באגים של USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"בחר להשבית ניפוי באגים ב-USB."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4f18bcb..2c1b9f4 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"通知"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"販売店デモ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB 接続"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"バックグラウンドで実行中のアプリ"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」がバックグラウンドで実行中です"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> 個のアプリがバックグラウンドで実行中です"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"タップすると、電池やデータの使用量を詳しく確認できます"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>、<xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"セーフモード"</string>
<string name="android_system_label" msgid="6577375335728551336">"Androidシステム"</string>
<string name="user_owner_label" msgid="1119010402169916617">"個人用に切り替える"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USBをMIDIに使用"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USBアクセサリを接続しました"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"タップしてその他のオプションを表示します。"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USBデバッグが接続されました"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"タップして USB デバッグを無効にします。"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USBデバッグを無効にする場合に選択します。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 31cd59b..d7183bc 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"გაფრთხილებები"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"დემო-რეჟიმი საცალო მოვაჭრეებისთვის"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB კავშირი"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"ფონურ რეჟიმში გაშვებული აპები"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> გაშვებულია ფონურ რეჟიმში"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"ფონურ რეჟიმში გაშვებულია <xliff:g id="NUMBER">%1$d</xliff:g> აპი"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"შეეხეთ ბატარეისა და მონაცემების მოხმარების შესახებ დეტალური ინფორმაციისთვის"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"უსაფრთხო რეჟიმი"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-ის სისტემა"</string>
<string name="user_owner_label" msgid="1119010402169916617">"პირად პროფილზე გადართვა"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI-სთვის"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"დაკავშირებულია USB აქსესუართან"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"შეეხეთ დამატებითი ვარიანტების სანახავად."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"აუდიო აქსესუარი მხარდაუჭერელია"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"შეეხეთ დამატებითი ინფორმაციისთვის"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB გამართვა შეერთებულია"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"შეეხეთ USB-გამართვის გასათიშად."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"მონიშნეთ რათა შეწყვიტოთ USB-ის გამართვა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index c512e43..842cd3a 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Дабылдар"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Бөлшек саудаға арналған демо нұсқасы"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB байланысы"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Фонда жұмыс істеп тұрған қолданбалар"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы фонда жұмыс істеп тұр"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> қолданба фонда жұмыс істеп тұр"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Батарея мен деректер трафигі туралы мәліметтер алу үшін түртіңіз"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Қауіпсіз режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android жүйесі"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Жекеге ауысу"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI режиміне арналған USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB жабдығына қосылған"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Қосымша опциялар үшін түртіңіз."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB жөндеу қосылған"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB түзетуін өшіру үшін түртіңіз."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 64306fd..a42f777 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"ការជូនដំណឹង"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"របៀបដាក់បង្ហាញក្នុងហាង"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"ការតភ្ជាប់ USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"កម្មវិធីដែលកំពុងដំណើរការនៅផ្ទៃខាងក្រោយ"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងដំណើរការនៅផ្ទៃខាងក្រោយ"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"កម្មវិធី <xliff:g id="NUMBER">%1$d</xliff:g> កំពុងដំណើរការនៅផ្ទៃខាងក្រោយ"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ចុចដើម្បីទទួលបានព័ត៌មានលម្អិតអំពីការប្រើប្រាស់ថ្ម និងទិន្នន័យ"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"របៀបសុវត្ថិភាព"</string>
<string name="android_system_label" msgid="6577375335728551336">"ប្រព័ន្ធ Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ប្តូរទៅផ្ទាល់ខ្លួន"</string>
@@ -1185,6 +1180,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB សម្រាប់ MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"បានភ្ជាប់ឧបករណ៍យូអេសប៊ី"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"ប៉ះសម្រាប់ជម្រើសជាច្រើនទៀត"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"បានភ្ជាប់ការកែកំហុសយូអេសប៊ី"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"ប៉ះដើម្បីបិទដំណើរការកែកំហុសយូអេសប៊ី"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ជ្រើស ដើម្បីបិទការកែកំហុសយូអេសប៊ី។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index aaabd91..fbd51b9 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"ಎಚ್ಚರಿಕೆಗಳು"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"ರಿಟೇಲ್ ಡೆಮೋ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB ಸಂಪರ್ಕ"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"ಅಪ್ಲಿಕೇಶನ್ಗಳು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿವೆ"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿದೆ"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗಳು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿವೆ"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ಬ್ಯಾಟರಿ ಮತ್ತು ಡೇಟಾ ಬಳಕೆಯ ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"ಸುರಕ್ಷಿತ ಮೋಡ್"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android ಸಿಸ್ಟಂ"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ವೈಯಕ್ತಿಕಗೆ ಬದಲಿಸಿ"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ಗೆ USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ಪರಿಕರಕ್ಕೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ಡೀಬಗಿಂಗ್ ಸಂಪರ್ಕ"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ab61857..bbddc24 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"알림"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"소매 데모"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB 연결"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"백그라운드에서 실행 중인 앱"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 백그라운드에서 실행 중"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g>개의 앱이 백그라운드에서 실행 중"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"탭하여 배터리 및 데이터 사용량에 관한 세부정보 보기"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"안전 모드"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 시스템"</string>
<string name="user_owner_label" msgid="1119010402169916617">"개인으로 전환"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI용 USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB 액세서리에 연결됨"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"옵션을 더 보려면 탭하세요."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB 디버깅 연결됨"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB 디버깅을 사용하지 않으려면 탭하세요."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB 디버깅을 사용하지 않으려면 선택합니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index a24bd7f..f5f48ad 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Эскертүүлөр"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Чекене соода дүкөнү үчүн демо режим"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB аркылуу туташуу"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Фондо иштеп жаткан колдонмолор"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу фондо иштөөдө"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> колдонмо фондо иштөөдө"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Батареянын кубаты жана Интернеттин колдонулушу жөнүндө билүү үчүн таптап коюңуз"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Коопсуз режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android тутуму"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Жеке профилге которулуу"</string>
@@ -283,7 +278,7 @@
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"байланыштарыңызды көрүүгө"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"түзмөктүн жайгашкан жерин аныктоого"</string>
- <string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнбарак"</string>
+ <string name="permgrouplab_calendar" msgid="5863508437783683902">"Жылнаама"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"жылнаамаңызды пайдалануу"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS билдирүүлөрдү жиберүү жана көрсөтүү"</string>
@@ -838,9 +833,9 @@
<string name="autofill_parish" msgid="8202206105468820057">"Пэриш"</string>
<string name="autofill_area" msgid="3547409050889952423">"Аймак"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"Эмират"</string>
- <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"желе бүктөмөлүрүңүздү жана тарыхыңызды окуу"</string>
+ <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"кыстармаларыңыз менен издөө таржымалыңызды карап көрүңүз"</string>
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Колдонмого Серепчи ачкан URLдердин тарыхын жана Серепчинин бүктөмөлөрүн окууга уруксат берет. Эскертүү: бул уруксат үчүнчү тараптык интернет-серепчилерге, же интернетке кирүү мүмкүнчүлүгү бар колдонмолорго таасир этпеши мүмкүн."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"желе бүктөмөлөрүн жана тарыхын жазуу"</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"кыстармалар жана издөө таржымалын өзгөртүү"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Колдонмого планшетиңизде сакталган Серепчинин тарыхын жана Серепчинин бүктөмөлөрүн өзгөртүү уруксатын берет. Бул колдонмого Серепчинин берилиштерин өчүрүү же өзгөртүү уруксатын берет. Эскертүү: бул уруксат үчүнчү тараптык интернет-серепчилерге, же интернетке кирүү мүмкүнчүлүгү бар колдонмолорго таасир этпеши мүмкүн."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Колдонмого Серепчиңиздин таржымалын же сыналгыңызда сакталган кыстармаларды өзгөртүү мүмкүнчүлүгүн берет. Ушуну менен, колдонмо Серепчи дайындарын тазалап же өзгөртө алат. Эскертүү: бул уруксат үчүнчү жактын серепчилери же башка желеде серептөө мүмкүнчүлүгү бар колдонмолор аркылуу иштетилбеши керек."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Колдонмого телефонуңузда сакталган Серепчинин тарыхын жана Серепчинин бүктөмөлөрүн өзгөртүү уруксатын берет. Бул колдонмого Серепчинин берилиштерин өчүрүү же өзгөртүү уруксатын берет. Эскертүү: бул уруксат үчүнчү тараптык интернет-серепчилерге, же интернетке кирүү мүмкүнчүлүгү бар колдонмолорго таасир этпеши мүмкүн."</string>
@@ -1128,7 +1123,7 @@
<string name="wifi_connect_default_application" msgid="7143109390475484319">"Колдонмо"</string>
<string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
<string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Дайректи иштетүү. Бул Wi-Fi клиентти/хотспотту өчүрөт."</string>
- <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Дайрект иштетилбеди."</string>
+ <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct иштетилген жок."</string>
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct иштөөдө"</string>
<string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Жөндөөлөрдү ачуу үчүн таптап коюңуз"</string>
<string name="accept" msgid="1645267259272829559">"Кабыл алуу"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI үчүн USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB аксессуарга байланышты"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Кошумча параметрлерди ачуу үчүн таптап коюңуз."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Мүчүлүштүктөрдү USB аркылуу оңдоо иштетилген"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Мүчүлштктрдү USB аркл оңдну өчр үчн тийп коюңуз."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
@@ -1351,7 +1350,7 @@
<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_mode_change" msgid="4547387741906537519">"Тартип алмаштыруу"</string>
+ <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Режимди өзгөртүү"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Кирүү"</string>
<string name="activitychooserview_choose_application" msgid="2125168057199941199">"Колдонмо тандоо"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index d162fe0..bf1ef11 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"ການເຕືອນ"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"ເດໂມສຳລັບຮ້ານຂາຍ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"ການເຊື່ອມຕໍ່ USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"ແອັບທີ່ກຳລັງເຮັດວຽກໃນພື້ນຫຼັງ"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງເຮັດວຽກໃນພື້ນຫຼັງ"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"ແອັບ <xliff:g id="NUMBER">%1$d</xliff:g> ແອັບກຳລັງເຮັດວຽກໃນພື້ນຫຼັງ"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ແຕະເພື່ອເບິ່ງລາຍລະອຽດການນຳໃຊ້ແບັດເຕີຣີ ແລະ ອິນເຕີເນັດ"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"ລະບົບ Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ສະລັບໄປໂປຣໄຟລ໌ສ່ວນຕົວ"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB ສຳລັບ MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"ເຊື່ອມຕໍ່ກັບອຸປະກອນເສີມ USB ແລ້ວ"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"ແຕະເພື່ອເບິ່ງຕົວເລືອກເພີ່ມເຕີມ."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"ເຊື່ອມຕໍ່ການດີບັ໊ກຜ່ານ USB ແລ້ວ"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"ແຕະເພື່ອປິດການດີບັກຜ່ານ USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ເລືອກເພື່ອປິດການດີບັ໊ກຜ່ານ USB."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 0a62d62..d8c292e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Įspėjimai"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demonstracinė versija mažmenininkams"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB jungtis"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Programos, veikiančios fone"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ veikia fone"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Programos (<xliff:g id="NUMBER">%1$d</xliff:g>) veikia fone"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Palieskite ir sužinokite išsamios informacijos apie akumuliatoriaus bei duomenų naudojimą"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Saugos režimas"</string>
<string name="android_system_label" msgid="6577375335728551336">"„Android“ sistema"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Perjungti į asmeninį režimą"</string>
@@ -1223,6 +1218,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB (MIDI)"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Prijungta prie USB priedo"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Palieskite, kad būtų rodoma daugiau parinkčių."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB derinimas prijungtas"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Palieskite, kad išjungtumėte USB derinimą."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pasirinkite, kas išjungtumėte USB derinimą."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3bcce6f..68a2299 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -268,16 +268,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Brīdinājumi"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demonstrācijas versija veikaliem"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB savienojums"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Lietotnes, kas darbojas fonā"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> darbojas fonā"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> lietotnes darbojas fonā"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Pieskarieties, lai skatītu detalizētu informāciju par akumulatora un datu lietojumu"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Drošais režīms"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistēma"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Pārslēgt personīgo profilu"</string>
@@ -1203,6 +1198,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB savienojums MIDI režīmā"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ir izveidots savienojums ar USB piederumu."</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Pieskarieties, lai skatītu citas iespējas."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB atkļūdošana ir pievienota."</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Pieskarieties, lai atspējotu USB atkļūdošanu."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Atlasiet, lai atspējotu USB atkļūdošanu."</string>
diff --git a/core/res/res/values-mcc204/config.xml b/core/res/res/values-mcc204/config.xml
new file mode 100644
index 0000000..790f768
--- /dev/null
+++ b/core/res/res/values-mcc204/config.xml
@@ -0,0 +1,24 @@
+<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <bool name="config_use_sim_language_file">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
index 63431a4..3049488 100755
--- a/core/res/res/values-mcc310-mnc004/config.xml
+++ b/core/res/res/values-mcc310-mnc004/config.xml
@@ -36,4 +36,7 @@
<bool name="config_auto_attach_data_on_creation">false</bool>
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
+
+ <bool name="config_use_sim_language_file">true</bool>
+
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index a210f5b..6f85081 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -62,4 +62,6 @@
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
+ <bool name="config_use_sim_language_file">true</bool>
+
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b5e8c90..ae79a4a 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Предупредувања"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Демонстрација за малопродажба"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-врска"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Апликациите се извршуваат во заднина"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> се извршува во заднина"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> апликации се извршуваат во заднина"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Допрете за детали за батеријата и потрошениот сообраќај"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Безбеден режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Систем Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Префрлете на личен профил"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"УСБ за МИДИ"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Поврзан со УСБ додаток"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Допрете за повеќе опции."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Поврзано е отстранување грешки преку УСБ"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Допрете за да се оневозможи отстранувањето грешки преку USB."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5dbb334..c1fc989 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"അലേർട്ടുകൾ"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"റീട്ടെയിൽ ഡെമോ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB കണക്ഷൻ"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"ആപ്പുകൾ പശ്ചാത്തലത്തിൽ റൺ ചെയ്യുന്നു"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> പശ്ചാത്തലത്തിൽ റൺ ചെയ്യുന്നു"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ആപ്പുകൾ പശ്ചാത്തലത്തിൽ റൺ ചെയ്യുന്നു"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ബാറ്ററി, ഡാറ്റ ഉപയോഗം എന്നിവയുടെ വിശദാംശങ്ങളറിയാൻ ടാപ്പുചെയ്യുക"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"സുരക്ഷിത മോഡ്"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android സിസ്റ്റം"</string>
<string name="user_owner_label" msgid="1119010402169916617">"വ്യക്തിഗത പ്രൊഫൈലിലേക്ക് മാറുക"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-യ്ക്കായുള്ള USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"ഒരു USB ആക്സസ്സറി കണക്റ്റുചെയ്തു"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ഡീബഗ്ഗിംഗ് കണക്റ്റുചെയ്തു"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ഡീബഗ്ഗിംഗ് പ്രവർത്തനരഹിതമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 765aa5d..3913964 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Сануулга"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Жижиглэнгийн жишээ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB холболт"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Цаана ажиллаж буй апп"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> цаана ажиллаж байна"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> апп цаана ажиллаж байна"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Батерей, дата ашиглалтын талаар дэлгэрэнгүй мэдээллийг харахын тулд товшино уу"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Аюулгүй горим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Андройд систем"</string>
<string name="user_owner_label" msgid="1119010402169916617">"\"Хувийн\" руу шилжих"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-ийн USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB төхөөрөмжид холбогдов"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Бусад сонголтыг харахын тулд товшино уу."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB дебаг холбогдсон"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB-н алдаа засварлахыг идэвхгүй болгохын тулд товшино уу."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB дебаг хийхийг идэвхгүй болгох бол сонгоно уу."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 4191828..1801664 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"सूचना"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"किरकोळ डेमो"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB कनेक्शन"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"अॅप्स बॅकग्राउंडमध्ये चालू आहेत"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> बॅकग्राउंडमध्ये चालू आहे"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> अॅप्स बॅकग्राउंडमध्ये चालू आहेत"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"बॅटरी आणि डेटा वापराच्या तपशीलांसाठी टॅप करा"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android सिस्टम"</string>
<string name="user_owner_label" msgid="1119010402169916617">"वैयक्तिकवर स्विच करा"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI साठी USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB उपसाधनावर कनेक्ट केले"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"अधिक पर्यायांसाठी टॅप करा."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग करणे कनेक्ट केले"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB डीबग करणे अक्षम करण्यासाठी टॅप करा."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index d4cc2d3..27095c5 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Makluman"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Tunjuk cara runcit"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Sambungan USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apl yang berjalan di latar belakang"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan di latar belakang"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apl sedang berjalan di latar belakang"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Ketik untuk mendapatkan butiran tentang penggunaan kuasa bateri dan data"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Beralih kepada Peribadi"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB untuk MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Disambungkan kepada aksesori USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Ketik untuk mendapatkan lagi pilihan."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Penyahpepijatan USB disambungkan"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Ketik untuk melumpuhkan penyahpepijatan USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pilih untuk melumpuhkan penyahpepijatan USB."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 1d89730..5d97ba8 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"သတိပေးချက်များ"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"လက်လီအရောင်းဆိုင် သရုပ်ပြမှု"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB ချိတ်ဆက်မှု"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"နောက်ခံတွင် ပွင့်နေသော အက်ပ်များ"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် နောက်ခံတွင် ပွင့်နေပါသည်"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"အက်ပ် <xliff:g id="NUMBER">%1$d</xliff:g> ခုသည် နောက်ခံတွင် ပွင့်နေပါသည်"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ဘက်ထရီနှင့် ဒေတာအသုံးပြုမှု အသေးစိတ်ကို ကြည့်ရန် တို့ပါ"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>၊ <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"အန္တရာယ်ကင်းမှု စနစ်(Safe mode)"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android စနစ်"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ကိုယ်ပိုင်သီးသန့်အဖြစ် ပြောင်းပါ"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI အတွက် USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USBတွဲဖက်ပစ္စည်းအား ချိတ်ဆက်ထားသည်"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"နောက်ထပ်ရွေးချယ်စရာများအတွက် တို့ပါ။"</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"အသံ ဆက်စပ်ပစ္စည်းကို မပံ့ပိုးပါ"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"နောက်ထပ် အချက်အလက်များအတွက် တို့ပါ"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားစစ်ခြင်းအား ချိတ်ဆက်ထားသည်"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ဆက်သွယ်ရေးစနစ်ကို ပိတ်ရန် တို့ပါ။"</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ab32ff8..e19a131 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Varsler"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Butikkdemo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-tilkobling"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apper kjører i bakgrunnen"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører i bakgrunnen"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apper kjører i bakgrunnen"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Trykk for detaljer om batteri- og databruk"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Bytt til den personlige profilen"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Koblet til et USB-tilbehør"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Trykk for å få flere alternativ."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-feilsøking tilkoblet"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Trykk for å slå av feilsøking via USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Velg for å deaktivere USB-debugging."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 33466bc..82b5c04 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"अलर्टहरू"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"खुद्रा बिक्री सम्बन्धी डेमो"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB जडान"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"पृष्ठभूमिमा चल्ने अनुप्रयोगहरू"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> पृष्ठभूमिमा चल्दैछ"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> अनुप्रयोगहरू पृष्ठभूमिमा चल्दैछन्"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ब्याट्री र डेटा प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"एन्ड्रोइड प्रणाली"</string>
<string name="user_owner_label" msgid="1119010402169916617">"व्यक्तिगत प्रोफाइलमा स्विच गर्नुहोस्"</string>
@@ -1189,6 +1184,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI को लागि USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB सहायकमा जोडिएको छ"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"थप विकल्पहरूका लागि ट्याप गर्नुहोस्।"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डिबग गर्ने जडित छ"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB डिबगिङलाई असक्षम गर्न ट्याप गर्नुहोस्।"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डिबगिङ असक्षम पार्न चयन गर्नुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c82219c..0d7d8c7 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Meldingen"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo voor de detailhandel"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-verbinding"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps uitgevoerd op achtergrond"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> wordt uitgevoerd op de achtergrond"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps worden uitgevoerd op de achtergrond"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tik voor informatie over batterij- en datagebruik"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Veilige modus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-systeem"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Overschakelen naar persoonlijk profiel"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB voor MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Aangesloten op een USB-accessoire"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tik voor meer opties."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audioaccessoire niet ondersteund"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tik voor meer informatie"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-foutopsporing verbonden"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tik om USB-foutopsporing uit te schakelen."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecteer deze optie om USB-foutopsporing uit te schakelen."</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 5c606ae..14a6ba1 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"ਸੁਚੇਤਨਾਵਾਂ"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"ਪ੍ਰਚੂਨ ਸਟੋਰਾਂ ਲਈ ਡੈਮੋ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB ਕਨੈਕਸ਼ਨ"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲਣ ਵਾਲੀਆਂ ਐਪਾਂ"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਹੀ ਹੈ"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ਐਪਾਂ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਹੀਆਂ ਹਨ"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"ਬੈਟਰੀ ਅਤੇ ਡੈਟਾ ਉਪਯੋਗ ਸਬੰਧੀ ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"ਸੁਰੱਖਿਅਤ ਮੋਡ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ਨਿੱਜੀ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ਲਈ USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"ਇੱਕ USB ਐਕਸੈਸਰੀ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"ਹੋਰ ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ਡੀਬਗਿੰਗ ਕਨੈਕਟ ਕੀਤੀ"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ਡੀਬੱਗਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7386f2d..ca0464c 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alerty"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Tryb demo dla sklepów"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Połączenie USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikacje działające w tle"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> działa w tle"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Aplikacje (<xliff:g id="NUMBER">%1$d</xliff:g>) działają w tle"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i transmisji danych"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Tryb awaryjny"</string>
<string name="android_system_label" msgid="6577375335728551336">"System Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Włącz profil osobisty"</string>
@@ -1223,6 +1218,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB w trybie MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Podłączono akcesorium USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Kliknij, by wyświetlić więcej opcji."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Podłączono moduł debugowania USB"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Kliknij, by wyłączyć debugowanie USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Wybierz, aby wyłączyć debugowanie USB."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d5dc395..fc58d2c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demonstração na loja"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Conexão USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps sendo executados em segundo plano"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está sendo executado em segundo plano"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps estão sendo executados em segundo plano"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tocar para ver detalhes sobre a bateria e o uso de dados"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Alternar para \"Pessoal\""</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a um acessório USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Toque para ver mais opções."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Acessório de áudio não compatível"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toque para mais informações"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Toque para desativar a depuração do USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração USB."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1f297c2..d8395bd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demonstração para retalho"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Ligação USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplicações em execução em segundo plano"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"A aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> está a ser executada em segundo plano"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicações estão a ser executadas em segundo plano"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Mudar para pessoal"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ligado a um acessório USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Toque para obter mais opções."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Acessório de áudio não suportado"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toque para obter mais informações"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB ligada"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Toque para desativar a depuração USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccione para desativar depuração USB."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d5dc395..fc58d2c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demonstração na loja"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Conexão USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Apps sendo executados em segundo plano"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está sendo executado em segundo plano"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> apps estão sendo executados em segundo plano"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tocar para ver detalhes sobre a bateria e o uso de dados"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Alternar para \"Pessoal\""</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a um acessório USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Toque para ver mais opções."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Acessório de áudio não compatível"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toque para mais informações"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Toque para desativar a depuração do USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração USB."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 3a2c38d..0c16186 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -268,16 +268,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alerte"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demonstrație comercială"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Conexiune USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplicațiile rulează în fundal"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează în fundal"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicații rulează în fundal"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Atingeți pentru mai multe detalii privind bateria și utilizarea datelor"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistemul Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Comutați la Personal"</string>
@@ -1203,6 +1198,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"Conexiune USB pentru MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectat la un accesoriu USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Atingeți pentru mai multe opțiuni."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accesoriul audio nu este acceptat"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Atingeți pentru mai multe informații"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depanarea USB este conectată"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Atingeți ca să dezactivați remedierea erorilor prin USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selectați pentru a dezactiva depanarea USB."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0bc71eb..7e6a8da 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Уведомления"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Деморежим для магазина"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-подключение"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Приложения, работающие в фоновом режиме"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" работает в фоновом режиме"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Несколько приложений (<xliff:g id="NUMBER">%1$d</xliff:g>) работает в фоновом режиме"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Нажмите, чтобы увидеть данные об энергопотреблении и объеме трафика"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасный режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Перейти в личный профиль"</string>
@@ -1223,6 +1218,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI через USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB-устройство подключено"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Нажмите, чтобы показать дополнительные параметры."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Отладка по USB разрешена"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Нажмите, чтобы отключить отладку по USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Нажмите, чтобы отключить отладку USB."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index b7e695a..33c384e 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"ඇඟවීම්"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"සිල්ලර ආදර්ශනය"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB සම්බන්ධතාවය"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"පසුබිමින් ධාවනය වන යෙදුම්"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> පසුබිමින් ධාවනය වේ"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"යෙදුම් <xliff:g id="NUMBER">%1$d</xliff:g>ක් පසුබිමින් ධාවනය වේ"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"බැටරි හා දත්ත භාවිතය පිළිබඳව විස්තර සඳහා තට්ටු කරන්න"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"ආරක්ෂිත ආකාරය"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android පද්ධතිය"</string>
<string name="user_owner_label" msgid="1119010402169916617">"පුද්ගලික වෙත මාරු වන්න"</string>
@@ -1185,6 +1180,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI සඳහා USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB මෙවලමකට සම්බන්ධිතයි"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"තවත් විකල්ප සඳහා තට්ටු කරන්න."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB නිදොස්කරණය සම්බන්ධිතයි"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB නිදොස්කරණය අබල කිරීමට තට්ටු කරන්න."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB නිදොස්කරණය අබල කිරීමට තෝරන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 018ea4e..0bc8164 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -96,7 +96,7 @@
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Žiadne hlasové hovory"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Hlasové ani tiesňové volania nie sú k dispozícii"</string>
<string name="RestrictedOnDataContent" msgid="8997474569390996587">"Váš operátor dočasne pozastavil dátovú službu na tomto mieste"</string>
- <string name="RestrictedOnEmergencyContent" msgid="4573217945494650061">"Váš operátor dočasne pozastavil núdzové hovory z tohto miesta"</string>
+ <string name="RestrictedOnEmergencyContent" msgid="4573217945494650061">"Váš operátor v tejto oblasti dočasne pozastavil tiesňové volania"</string>
<string name="RestrictedOnNormalContent" msgid="1579434198284512182">"Váš operátor dočasne pozastavil hlasové hovory z tohto miesta"</string>
<string name="RestrictedOnAllVoiceContent" msgid="5243580774142557047">"Váš operátor v tejto oblasti dočasne blokuje hlasové a tiesňové hovory"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="4008877505368566980">"Nepodarilo sa pripojiť k sieti"</string>
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Upozornenia"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Predajná ukážka"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Pripojenie USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikácie sú spustené na pozadí"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je spustená na pozadí"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Niekoľko aplikácií (<xliff:g id="NUMBER">%1$d</xliff:g>) je spustených na pozadí"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Klepnutím zobrazíte podrobnosti o batérii a spotrebe dát"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Núdzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Prepnúť na osobný"</string>
@@ -1223,6 +1218,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB na pripojenie zariadenia MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Pripojené k periférnemu zariadeniu USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Klepnutím zobrazíte ďalšie možnosti."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Zvukové príslušenstvo nie je podporované"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Ďalšie informácie zobrazíte klepnutím"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ladenie cez USB pripojené"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Klepnutím zakážete ladenie cez USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Výberom zakážete ladenie USB."</string>
@@ -1825,7 +1822,7 @@
<string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Zachovajte pokoj a vyhľadajte úkryt v okolí."</string>
<string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Okamžite začnite evakuáciu z prímorských a nábrežných oblastí na bezpečnejšie miesto, napríklad do vyššie položených regiónov."</string>
<string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Zachovajte pokoj a vyhľadajte úkryt v okolí."</string>
- <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test núdzových správ"</string>
+ <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test tiesňových správ"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
<string name="mmcc_authentication_reject" msgid="7729819349669603406">"SIM karta je zakázaná"</string>
<string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"SIM karta nie je k dispozícii"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 729612f..8ac1b4d 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Opozorila"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Predstavitev za maloprodajo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Povezava USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikacije se izvajajo v ozadju"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> se izvaja v ozadju"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Več aplikacij (<xliff:g id="NUMBER">%1$d</xliff:g>) se izvaja v ozadju"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Dotaknite se za prikaz podrobnosti porabe akumulatorja in prenosa podatkov"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Varni način"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Preklop na osebni profil"</string>
@@ -1223,6 +1218,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Priključen na dodatek USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Dotaknite se za več možnosti."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Iskanje napak prek USB je povezano"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Dotaknite se za izklop odpravljanja napak prek USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Izberite, če želite onemogočiti iskanje in odpravljanje napak prek vrat USB."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 563f4bd..9f2bae8 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Sinjalizimet"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demonstrimi i shitjes me pakicë"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Lidhja USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Aplikacionet që ekzekutohen në sfond"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> po ekzekutohet në sfond"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikacione po ekzekutohen në sfond"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Trokit për detaje për baterinë dhe përdorimin e të dhënave"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modaliteti i sigurisë"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistemi \"android\""</string>
<string name="user_owner_label" msgid="1119010402169916617">"Ndryshoje te \"Personale\""</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB për MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"U lidh me një ndihmës USB-je"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Trokit për më shumë opsione."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Korrigjuesi i USB-së i lidhur"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Trokit për të çaktivizuar korrigjimin e gabimeve të USB-së."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 2d2cc70..ee2c4c8 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -268,16 +268,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Обавештења"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Режим демонстрације за малопродајне објекте"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB веза"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Апликације покренуте у позадини"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> је покренута у позадини"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Апликације (<xliff:g id="NUMBER">%1$d</xliff:g>) су покренуте у позадини"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Додирните да бисте прегледали детаље о батерији и потрошњи података"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Безбедни режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android систем"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Пређи на Лични профил"</string>
@@ -300,13 +295,13 @@
<string name="permgroupdesc_phone" msgid="6234224354060641055">"упућује телефонске позиве и управља њима"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Сензори за тело"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"приступа подацима сензора о виталним функцијама"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Преузима садржај прозора"</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_title_canRequestTouchExploration" msgid="3108723364676667320">"да укључи Истраживања додиром"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"Ставке које додирнете ће бити изговорене наглас, а можете да се крећете по екрану покретима."</string>
- <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Прати текст који уносите"</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_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>
@@ -1053,7 +1048,7 @@
<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_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%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>
<string name="aerr_report" msgid="5371800241488400617">"Пошаљите повратне информације"</string>
@@ -1203,6 +1198,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Повезано са USB додатком"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Додирните за још опција."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Додатна опрема за аудио садржај није подржана"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Додирните за више информација"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Отклањање грешака са USB-а је успостављено"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Додирните да бисте онемогућили отклањање грешака са USB-а."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изаберите да бисте онемогућили отклањања грешака са USB-а."</string>
@@ -1312,7 +1309,7 @@
<string name="vpn_lockdown_config" msgid="5099330695245008680">"Додирните да бисте подесили"</string>
<string name="upload_file" msgid="2897957172366730416">"Одабери датотеку"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Није изабрана ниједна датотека"</string>
- <string name="reset" msgid="2448168080964209908">"Поново постави"</string>
+ <string name="reset" msgid="2448168080964209908">"Ресетуј"</string>
<string name="submit" msgid="1602335572089911941">"Пошаљи"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Режим рада у аутомобилу је омогућен"</string>
<string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Додирните да бисте изашли из режима рада у аутомобилу."</string>
@@ -1346,7 +1343,7 @@
<string name="sync_really_delete" msgid="2572600103122596243">"Избриши ставке"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Опозови брисања"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Не ради ништа за сада"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"Избор налога"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"Изаберите налог"</string>
<string name="add_account_label" msgid="2935267344849993553">"Додај налог"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"Додај налог"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Повећавање"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index c480df4..0c13829 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Varningar"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo för återförsäljare"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB-anslutning"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Appar körs i bakgrunden"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs i bakgrunden"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> appar körs i bakgrunden"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Tryck för information om batteri- och dataanvändning"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Säkert läge"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Byt till din personliga profil"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB för MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ansluten till ett USB-tillbehör"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Tryck för fler alternativ."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-felsökning ansluten"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Tryck om du vill inaktivera USB-felsökning."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Välj att inaktivera USB-felsökning."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index cec75bc..8c563a1 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -263,16 +263,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Arifa"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Onyesho la duka la rejareja"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Muunganisho wa USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Programu zinatumika chinichini"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumika chinichini"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Programu <xliff:g id="NUMBER">%1$d</xliff:g> zinatumika chinichini"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Gonga ili upate maelezo kuhusu betri na matumizi ya data"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
<string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Badili uweke wasifu wa Binafsi"</string>
@@ -1181,6 +1176,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB kwa ajili ya MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Imeunganishwa kwa kifuasi cha USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Gonga ili upate chaguo zaidi."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Kifuasi cha sauti hakiwezi kutumika"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Gonga ili upate maelezo zaidi"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Utatuaji wa USB umeunganishwa"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Gonga ili uzime utatuaji wa USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chagua ili kulemaza utatuaji USB."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 7062e98..45da7ef 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"விழிப்பூட்டல்கள்"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"விற்பனையாளர் டெமோ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB இணைப்பு"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"பின்னணியில் இயங்கும் பயன்பாடுகள்"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாடு பின்னணியில் இயங்குகிறது"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> பயன்பாடுகள் பின்னணியில் இயங்குகின்றன"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"பேட்டரி மற்றும் தரவு உபயோகம் குறித்த விவரங்களுக்கு, தட்டவும்"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"பாதுகாப்பு பயன்முறை"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android அமைப்பு"</string>
<string name="user_owner_label" msgid="1119010402169916617">"தனிப்பட்ட சுயவிவரத்திற்கு மாறு"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB, MIDIக்கு மட்டும்"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB துணைக்கருவியுடன் இணைக்கப்பட்டுள்ளது"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"மேலும் விருப்பங்களுக்கு, தட்டவும்."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ஆடியோ துணைக்கருவி ஆதரிக்கப்படவில்லை"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"மேலும் தகவலுக்கு, தட்டவும்"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB பிழைதிருத்தம் இணைக்கப்பட்டது"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB பிழை திருத்தத்தை முடக்க, தட்டவும்."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 31e2721..3fbb340 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"హెచ్చరికలు"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"రిటైల్ డెమో"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB కనెక్షన్"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"నేపథ్యంలో అమలు అవుతున్న ఆప్లు"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> నేపథ్యంలో అమలు అవుతోంది"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ఆప్లు నేపథ్యంలో అమలు అవుతున్నాయి"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"బ్యాటరీ మరియు డేటా వినియోగ వివరాల కోసం నొక్కండి"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"సురక్షిత మోడ్"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android సిస్టమ్"</string>
<string name="user_owner_label" msgid="1119010402169916617">"వ్యక్తిగతానికి మార్చు"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI కోసం USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ఉపకరణానికి కనెక్ట్ చేయబడింది"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"మరిన్ని ఎంపికల కోసం నొక్కండి."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB డీబగ్గింగ్ కనెక్ట్ చేయబడింది"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB డీబగ్గింగ్ను నిలిపివేయడానికి నొక్కండి."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index fd7158f..2278ca8 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"การแจ้งเตือน"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"การสาธิตสำหรับผู้ค้าปลีก"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"การเชื่อมต่อ USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"แอปที่กำลังทำงานในเบื้องหลัง"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงานในเบื้องหลัง"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"แอป <xliff:g id="NUMBER">%1$d</xliff:g> กำลังทำงานในเบื้องหลัง"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"แตะเพื่อดูรายละเอียดเกี่ยวกับแบตเตอรี่และปริมาณการใช้อินเทอร์เน็ต"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"โหมดปลอดภัย"</string>
<string name="android_system_label" msgid="6577375335728551336">"ระบบ Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"เปลี่ยนไปใช้โปรไฟล์ส่วนตัว"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB สำหรับ MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"เชื่อมต่อกับอุปกรณ์เสริม USB แล้ว"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"แตะเพื่อดูตัวเลือกเพิ่มเติม"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"เชื่อมต่อการแก้ไขข้อบกพร่อง USB แล้ว"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"แตะเพื่อปิดใช้การแก้ไขข้อบกพร่องของ USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"เลือกเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e58f62f..c4d40d1 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Mga Alerto"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Retail demo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Koneksyon ng USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Tumatakbo ang mga app sa background"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g> sa background"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> (na) app ang tumatakbo sa background"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"I-tap para sa mga detalye tungkol sa paggamit ng baterya at data"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Lumipat sa Personal"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para sa MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Nakakonekta sa isang accessory ng USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"I-tap para sa higit pang mga opsyon."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Konektado ang debugging ng USB"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"I-tap upang i-disable ang pag-debug ng USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Piliin upang i-disable ang debugging ng USB."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e3954359..c94fbd4 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Uyarılar"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Mağaza demo"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB bağlantısı"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Uygulamalar arka planda çalışıyor"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> arka planda çalışıyor"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> uygulama arka planda çalışıyor"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Pil ve veri kullanımı ile ilgili ayrıntılar için dokunun"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android Sistemi"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Kişisel Profile Geç"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI için USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB aksesuarına bağlandı"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Diğer seçenekler için dokunun."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB hata ayıklaması bağlandı"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB hata ayıklama özelliğini devre dışı bırakmak için dokunun."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB hata ayıklamasını devre dışı bırakmak için tıklayın."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 09cec2a..ac36114 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -271,16 +271,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Сповіщення"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Демо-режим для роздрібної торгівлі"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"З’єднання USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Додатки, які працюють у фоновому режимі"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> працює у фоновому режимі"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"Додатки, які працюють у фоновому режимі: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Торкніться, щоб переглянути інформацію про використання заряду акумулятора та даних"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Безп. режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Перейти в особистий профіль"</string>
@@ -1223,6 +1218,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB для режиму MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Під’єднано до аксесуара USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Торкніться, щоб переглянути більше опцій."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аксесуар для аудіо не підтримується"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Торкніться, щоб дізнатися більше"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Налагодження USB завершено"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Торкніться, щоб вимкнути налагодження USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Вибер., щоб вимкн. налагодж. USB."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 225faa6..4722203 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"الرٹس"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"ریٹیل ڈیمو"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB کنکشن"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"ایپس پس منظر میں چل رہی ہیں"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> پس منظر میں چل رہی ہے"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ایپس پس منظر میں چل رہی ہیں"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"بیٹری اور ڈیٹا استعمال کے بارے میں تفصیلات کے لیے تھپتھپائیں"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>، <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"حفاظتی وضع"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android سسٹم"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ذاتی پر سوئچ کریں"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI کیلئے USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"ایک USB لوازم سے مربوط ہے"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"مزید اختیارات کیلئے تھپتھپائیں۔"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ڈیبگ کرنا مربوط ہو گیا"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ڈیبگنگ کو غیر فعال کرنے کیلئے تھپتھپائیں۔"</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 0e7d4ff..1fdfc45 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -72,7 +72,7 @@
<string name="ClirMmi" msgid="7784673673446833091">"Chiquvchi raqami"</string>
<string name="ColpMmi" msgid="3065121483740183974">"Qo‘ng‘iroq qiluvchining raqami"</string>
<string name="ColrMmi" msgid="4996540314421889589">"Qo‘ng‘iroq qiluvchining raqamini cheklash"</string>
- <string name="CfMmi" msgid="5123218989141573515">"Chaqiruvni yo‘naltirish"</string>
+ <string name="CfMmi" msgid="5123218989141573515">"Chaqiruvlarni uzatish"</string>
<string name="CwMmi" msgid="9129678056795016867">"Chaqiruvni kutish"</string>
<string name="BaMmi" msgid="455193067926770581">"Qo‘ng‘iroqlarni taqiqlash"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Parolni o‘zgartirish"</string>
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Ogohlantirishlar"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo rejim"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB orqali ulanish"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Fonda ishlayotgan ilovalar"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> fonda ishlamoqda"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ta ilova fonda ishlamoqda"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Batareya va trafik sarfiga oid tafsilotlarni olish uchun ustiga bosing"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Xavfsiz usul"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android tizimi"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Shaxsiy profilga o‘tish"</string>
@@ -1083,15 +1078,15 @@
<string name="dump_heap_title" msgid="5864292264307651673">"Hip-damp ma’lumotlari bilan ulashasizmi?"</string>
<string name="dump_heap_text" msgid="4809417337240334941">"<xliff:g id="PROC">%1$s</xliff:g> jarayoni o‘zi uchun ajratilgan <xliff:g id="SIZE">%2$s</xliff:g> xotira chegarasidan o‘tib ketdi. Ilova dasturchisi bilan ulashishingiz uchun hip-damp ma’lumotlari yig‘ilib qoldi. Ehtiyot bo\'ling: ushbu hip-dampda ilova uchun foydalanishga ruxsat berilgan shaxsiy ma’lumotlaringiz bo‘lishi mumkin."</string>
<string name="sendText" msgid="5209874571959469142">"Matn uchun amalni tanlash"</string>
- <string name="volume_ringtone" msgid="6885421406845734650">"Jiringlaganda ovoz balandligi"</string>
+ <string name="volume_ringtone" msgid="6885421406845734650">"Jiringlaganda tovush balandligi"</string>
<string name="volume_music" msgid="5421651157138628171">"Multimedia ovozi"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Bluetooth orqali ijro etilmoqda"</string>
<string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Ovozsiz rejim tanlandi"</string>
- <string name="volume_call" msgid="3941680041282788711">"Suhbat vaqtidagi ovoz balandligi"</string>
+ <string name="volume_call" msgid="3941680041282788711">"Suhbat vaqtidagi tovush balandligi"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Kiruvchi bluetooth tovushi"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Signal ovozi"</string>
<string name="volume_notification" msgid="2422265656744276715">"Eslatma tovushi"</string>
- <string name="volume_unknown" msgid="1400219669770445902">"Ovoz balandligi"</string>
+ <string name="volume_unknown" msgid="1400219669770445902">"Tovush balandligi"</string>
<string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Bluetooth tovushi"</string>
<string name="volume_icon_description_ringer" msgid="3326003847006162496">"Rington balandligi"</string>
<string name="volume_icon_description_incall" msgid="8890073218154543397">"Qo‘ng‘iroq tovushi balandligi"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB orqali MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB jihozga ulangan"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Boshqa parametrlarini ko‘rish uchun bosing."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio aksessuar ishlamaydi"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tafsilotlar uchun bosing"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB orqali nosozliklarni tuzatish"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Faolsizlantirish uchun bu yerga bosing."</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
@@ -1474,7 +1471,7 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Siz grafik kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri chizdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"O‘chirish"</string>
- <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Ovoz balandligi tavsiya etilgan darajadan ham yuqori ko‘tarilsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin."</string>
+ <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Tovush balandligi tavsiya etilgan darajadan ham yuqori qilinsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Tezkor ishga tushirishdan foydalanilsinmi?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Maxsus imkoniyatlar funksiyasidan foydalanish uchun u yoniqligida ikkala ovoz balandligini boshqarish tugmasini 3 soniya bosib turing.\n\n Joriy maxsus imkoniyatlar funksiyasi:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Bu funksiyani Sozlamalar > Maxsus imkoniyatlar orqali o‘zgartirish mumkin."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Tezkor ishga tushirishni o‘chirib qo‘yish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index f2ba671..c39901f 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Cảnh báo"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Giới thiệu bán lẻ"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Kết nối USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Ứng dụng đang chạy trong nền"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang chạy trong nền"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng đang chạy trong nền"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Nhấn để biết chi tiết về pin và mức sử dụng dữ liệu"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Chế độ an toàn"</string>
<string name="android_system_label" msgid="6577375335728551336">"Hệ thống Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Chuyển sang Cá nhân"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB cho MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Đã kết nối với phụ kiện USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Nhấn để biết thêm tùy chọn."</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"Gỡ lỗi USB đã được kết nối"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Nhấn để vô hiệu hóa gỡ lỗi USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chọn để vô hiệu hóa gỡ lỗi USB."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6e35e3f..8031be6 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"提醒"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"零售演示模式"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB 连接"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"在后台运行的应用"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在后台运行"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> 个应用正在后台运行"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"点按即可详细了解电量和流量消耗情况"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>、<xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
<string name="user_owner_label" msgid="1119010402169916617">"切换到“个人”"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"正在通过 USB 连接到 MIDI 接口"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"已连接到USB配件"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"点按即可查看更多选项。"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"已连接到USB调试"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"点按即可停用 USB 调试功能。"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"选择停用USB调试。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 74f4987..3dc0cd08 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"通知"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"零售示範"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB 連線"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"正在背景中執行的應用程式"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> 正在背景中執行"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在背景中執行"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"輕按即可查看電池和數據用量詳情"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>、<xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
<string name="user_owner_label" msgid="1119010402169916617">"切換至個人設定檔"</string>
@@ -701,7 +696,7 @@
<string name="relationTypeSister" msgid="1735983554479076481">"姊妹"</string>
<string name="relationTypeSpouse" msgid="394136939428698117">"配偶"</string>
<string name="sipAddressTypeCustom" msgid="2473580593111590945">"自訂"</string>
- <string name="sipAddressTypeHome" msgid="6093598181069359295">"家用"</string>
+ <string name="sipAddressTypeHome" msgid="6093598181069359295">"住宅"</string>
<string name="sipAddressTypeWork" msgid="6920725730797099047">"公司"</string>
<string name="sipAddressTypeOther" msgid="4408436162950119849">"其他"</string>
<string name="quick_contacts_not_available" msgid="746098007828579688">"找不到可以查看這位聯絡人的應用程式。"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"已連接到一個 USB 配件"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"輕按即可查看更多選項。"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"已連接 USB 偵錯工具"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"輕按即可停用 USB 偵錯功能。"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"選取即可停用 USB 偵錯。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 87e98dd..2916c42 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"快訊"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"零售商示範模式"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB 連線"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"在背景執行的應用程式"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在背景執行"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在背景執行"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"輕觸即可取得電池和數據用量的詳細資料"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>、<xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
<string name="user_owner_label" msgid="1119010402169916617">"切換至個人設定檔"</string>
@@ -1183,6 +1178,10 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"已連接 USB 配件"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"輕觸即可查看更多選項。"</string>
+ <!-- no translation found for usb_unsupported_audio_accessory_title (2256529893240208458) -->
+ <skip />
+ <!-- no translation found for usb_unsupported_audio_accessory_message (7811865061127547035) -->
+ <skip />
<string name="adb_active_notification_title" msgid="6729044778949189918">"已連接 USB 偵錯工具"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"輕觸即可停用 USB 偵錯。"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"選取以停用 USB 偵錯。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a656e6c..9da0d4f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -265,16 +265,11 @@
<string name="notification_channel_alerts" msgid="4496839309318519037">"Izexwayiso"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Idemo yokuthenga"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Ukuxhumeka kwe-USB"</string>
- <!-- no translation found for notification_channel_foreground_service (6665375982962336520) -->
- <skip />
- <!-- no translation found for foreground_service_app_in_background (6826789589341671842) -->
- <skip />
- <!-- no translation found for foreground_service_apps_in_background (7150914856893450380) -->
- <skip />
- <!-- no translation found for foreground_service_tap_for_details (372046743534354644) -->
- <skip />
- <!-- no translation found for foreground_service_multiple_separator (4021901567939866542) -->
- <skip />
+ <string name="notification_channel_foreground_service" msgid="6665375982962336520">"Izinhlelo zokusebenza zisebenza ngasemuva"</string>
+ <string name="foreground_service_app_in_background" msgid="6826789589341671842">"<xliff:g id="APP_NAME">%1$s</xliff:g> iyasebenza ngemuva"</string>
+ <string name="foreground_service_apps_in_background" msgid="7150914856893450380">"<xliff:g id="NUMBER">%1$d</xliff:g> izinhlelo zokusebenza ziyasebenza ngemuva"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Thepha ngemininingwane ekusetshenzisweni kwebhethri nedatha"</string>
+ <string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Imodi ephephile"</string>
<string name="android_system_label" msgid="6577375335728551336">"Uhlelo lwe-Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Shintshela komuntu siqu"</string>
@@ -1183,6 +1178,8 @@
<string name="usb_midi_notification_title" msgid="4850904915889144654">"I-USB ye-MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ixhunywe ku-accessory ye-USB"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"Thepha ngezinketho eziningi."</string>
+ <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Insiza yomsindo ayisekelwa"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Thepha ngolwazi olungeziwe"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ukulungisa iphutha le-USB kuxhunyiwe"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"Thepha ukuze ukhubaze ukususa isiphazamisi se-USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Khetha ukuvimbela ukulungisa iphutha le-USB."</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f3c54ba..12fa8fc 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1265,6 +1265,49 @@
<!-- True if WallpaperService is enabled -->
<bool name="config_enableWallpaperService">true</bool>
+ <!-- Enables the TimeZoneRuleManager service. This is the master switch for the updateable time
+ zone update mechanism. -->
+ <bool name="config_enableUpdateableTimeZoneRules">false</bool>
+
+ <!-- Enables APK-based time zone update triggering. Set this to false when updates are triggered
+ via external events and not by APK updates. For example, if an updater checks with a server
+ on a regular schedule.
+ [This is only used if config_enableUpdateableTimeZoneRules is true.] -->
+ <bool name="config_timeZoneRulesUpdateTrackingEnabled">false</bool>
+
+ <!-- The package of the time zone rules updater application. Expected to be the same
+ for all Android devices that support APK-based time zone rule updates.
+ A package-targeted android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent
+ will be sent to the updater app if the system server detects an update to the updater or
+ data app packages.
+ The package referenced here must have the android.permission.UPDATE_TIME_ZONE_RULES
+ permission.
+ [This is only used if config_enableUpdateableTimeZoneRules and
+ config_timeZoneRulesUpdateTrackingEnabled are true.] -->
+ <string name="config_timeZoneRulesUpdaterPackage" translateable="false"></string>
+
+ <!-- The package of the time zone rules data application. Expected to be configured
+ by OEMs to reference their own priv-app APK package.
+ A package-targeted android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent
+ will be sent to the updater app if the system server detects an update to the updater or
+ data app packages.
+ [This is only used if config_enableUpdateableTimeZoneRules and
+ config_timeZoneRulesUpdateTrackingEnabled are true.] -->
+ <string name="config_timeZoneRulesDataPackage" translateable="false"></string>
+
+ <!-- The allowed time in milliseconds between an update check intent being broadcast and the
+ response being considered overdue. Reliability triggers will not fire in this time.
+ [This is only used if config_enableUpdateableTimeZoneRules and
+ config_timeZoneRulesUpdateTrackingEnabled are true.] -->
+ <!-- 5 minutes -->
+ <integer name="config_timeZoneRulesCheckTimeMillisAllowed">300000</integer>
+
+ <!-- The number of times a time zone update check is allowed to fail before the system will stop
+ reacting to reliability triggers.
+ [This is only used if config_enableUpdateableTimeZoneRules and
+ config_timeZoneRulesUpdateTrackingEnabled are true.] -->
+ <integer name="config_timeZoneRulesCheckRetryCount">5</integer>
+
<!-- Whether to enable network location overlay which allows network
location provider to be replaced by an app at run-time. When disabled,
only the config_networkLocationProviderPackageName package will be
@@ -1299,9 +1342,9 @@
* Include a Service for the android.net.scoring.RECOMMEND_NETWORKS action
protected by the BIND_NETWORK_RECOMMENDATION_SERVICE permission.
- This must be set to a valid network recommendation app.
+ This must be set to a valid network recommendation app or empty.
-->
- <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false">com.android.networkrecommendation</string>
+ <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
<!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be
replaced by an app at run-time. When disabled, only the
@@ -2886,4 +2929,8 @@
<!-- Additional non-platform defined secure settings exposed to Instant Apps -->
<string-array name="config_allowedSecureInstantAppSettings"></string-array>
+
+ <!-- Handle volume keys directly in Window Manager without passing them to the foreground app -->
+ <bool name="config_handleVolumeKeysInWindowManager">false</bool>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9848485..134527c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -644,8 +644,10 @@
<!-- Text shown when viewing channel settings for notifications related to a usb connection -->
<string name="notification_channel_usb">USB connection</string>
- <!-- Text shown when viewing channel settings for notifications related to running foreground
- services [CHAR LIMIT=NONE] -->
+ <!-- This is the label for the notification channel settings that controls the behavior
+ of the notification about applications that are running in the background (that is,
+ perhaps confusingly, running foreground services but not the foreground UI on the screen).
+ [CHAR LIMIT=NONE] -->
<string name="notification_channel_foreground_service">Apps running in background</string>
<!-- Label for foreground service notification when one app is running. [CHAR LIMIT=NONE] -->
@@ -663,7 +665,10 @@
data usage</string>
<!-- Separator for foreground service notification content listing all apps when there
- are multiple apps running [CHAR LIMIT=NONE] -->
+ are multiple apps running. The left and right side may both already be compound
+ (constructed using this separator). Should be kept as short as possible, this is
+ for summary text in the notification where there is not a lot of space.
+ [CHAR LIMIT=NONE] -->
<string name="foreground_service_multiple_separator"><xliff:g id="left_side">%1$s</xliff:g>,
<xliff:g id="right_side">%2$s</xliff:g></string>
@@ -2696,10 +2701,10 @@
<string name="dial">Phone</string>
<!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] -->
- <string name="map">Map</string>
+ <string name="map">Maps</string>
<!-- Label for item in the text selection menu to trigger a Browser app [CHAR LIMIT=20] -->
- <string name="browse">Browse</string>
+ <string name="browse">Browser</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 2ae2ca0..690b051 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -511,6 +511,8 @@
<style name="Widget.CheckedTextView">
<item name="textAlignment">viewStart</item>
+ <item name="breakStrategy">high_quality</item>
+ <item name="hyphenationFrequency">normal</item>
</style>
<style name="Widget.TextView.ListSeparator">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5aec3ce..8ff7f67 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -284,6 +284,12 @@
<java-symbol type="bool" name="split_action_bar_is_narrow" />
<java-symbol type="bool" name="config_useVolumeKeySounds" />
<java-symbol type="bool" name="config_enableWallpaperService" />
+ <java-symbol type="bool" name="config_enableUpdateableTimeZoneRules" />
+ <java-symbol type="bool" name="config_timeZoneRulesUpdateTrackingEnabled" />
+ <java-symbol type="string" name="config_timeZoneRulesUpdaterPackage" />
+ <java-symbol type="string" name="config_timeZoneRulesDataPackage" />
+ <java-symbol type="integer" name="config_timeZoneRulesCheckTimeMillisAllowed" />
+ <java-symbol type="integer" name="config_timeZoneRulesCheckRetryCount" />
<java-symbol type="bool" name="config_sendAudioBecomingNoisy" />
<java-symbol type="bool" name="config_enableScreenshotChord" />
<java-symbol type="bool" name="config_bluetooth_default_profiles" />
@@ -3022,4 +3028,5 @@
<java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
<java-symbol type="array" name="config_allowedSecureInstantAppSettings" />
+ <java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" />
</resources>
diff --git a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
new file mode 100644
index 0000000..9bbcd3d
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link DistroFormatVersion}.
+ */
+// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
+public class DistroFormatVersionTest {
+
+ @Test
+ public void equalsAndHashCode() {
+ DistroFormatVersion one = new DistroFormatVersion(1, 2);
+ assertEqualsContract(one, one);
+
+ DistroFormatVersion two = new DistroFormatVersion(1, 2);
+ assertEqualsContract(one, two);
+
+ DistroFormatVersion three = new DistroFormatVersion(2, 1);
+ assertFalse(one.equals(three));
+ }
+
+ @Test
+ public void parcelable() {
+ DistroFormatVersion version = new DistroFormatVersion(2, 3);
+
+ Parcel parcel = Parcel.obtain();
+ version.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+
+ DistroFormatVersion newVersion = DistroFormatVersion.CREATOR.createFromParcel(parcel);
+
+ assertEquals(version, newVersion);
+ }
+
+ @Test
+ public void supportsVersion() {
+ DistroFormatVersion deviceVersion = new DistroFormatVersion(2, 2);
+ assertTrue(deviceVersion.supports(deviceVersion));
+
+ DistroFormatVersion sameVersion = new DistroFormatVersion(2, 2);
+ assertTrue(deviceVersion.supports(sameVersion));
+
+ // Minor versions are backwards compatible.
+ DistroFormatVersion sameMajorNewerMinor = new DistroFormatVersion(2, 3);
+ assertTrue(deviceVersion.supports(sameMajorNewerMinor));
+ DistroFormatVersion sameMajorOlderMinor = new DistroFormatVersion(2, 1);
+ assertFalse(deviceVersion.supports(sameMajorOlderMinor));
+
+ // Major versions are not backwards compatible.
+ DistroFormatVersion newerMajor = new DistroFormatVersion(1, 2);
+ assertFalse(deviceVersion.supports(newerMajor));
+ DistroFormatVersion olderMajor = new DistroFormatVersion(3, 2);
+ assertFalse(deviceVersion.supports(olderMajor));
+ }
+
+ private static void assertEqualsContract(DistroFormatVersion one, DistroFormatVersion two) {
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
new file mode 100644
index 0000000..2fbc9a1
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link DistroRulesVersion}.
+ */
+// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
+public class DistroRulesVersionTest {
+
+ @Test
+ public void equalsAndHashCode() {
+ DistroRulesVersion one = new DistroRulesVersion("2016a", 2);
+ assertEqualsContract(one, one);
+
+ DistroRulesVersion two = new DistroRulesVersion("2016a", 2);
+ assertEqualsContract(one, two);
+
+ DistroRulesVersion three = new DistroRulesVersion("2016b", 1);
+ assertFalse(one.equals(three));
+ }
+
+ @Test
+ public void parcelable() {
+ DistroRulesVersion version = new DistroRulesVersion("2016a", 2);
+
+ Parcel parcel = Parcel.obtain();
+ version.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+
+ DistroRulesVersion newVersion = DistroRulesVersion.CREATOR.createFromParcel(parcel);
+
+ assertEquals(version, newVersion);
+ }
+
+ @Test
+ public void isOlderThan() {
+ DistroRulesVersion deviceVersion = new DistroRulesVersion("2016b", 2);
+ assertFalse(deviceVersion.isOlderThan(deviceVersion));
+
+ DistroRulesVersion sameVersion = new DistroRulesVersion("2016b", 2);
+ assertFalse(deviceVersion.isOlderThan(sameVersion));
+
+ DistroRulesVersion sameRulesNewerRevision = new DistroRulesVersion("2016b", 3);
+ assertTrue(deviceVersion.isOlderThan(sameRulesNewerRevision));
+
+ DistroRulesVersion sameRulesOlderRevision = new DistroRulesVersion("2016b", 1);
+ assertFalse(deviceVersion.isOlderThan(sameRulesOlderRevision));
+
+ DistroRulesVersion newerRules = new DistroRulesVersion("2016c", 2);
+ assertTrue(deviceVersion.isOlderThan(newerRules));
+
+ DistroRulesVersion olderRules = new DistroRulesVersion("2016a", 2);
+ assertFalse(deviceVersion.isOlderThan(olderRules));
+ }
+
+ private static void assertEqualsContract(DistroRulesVersion one, DistroRulesVersion two) {
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
new file mode 100644
index 0000000..a9357c9
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link RulesState}.
+ */
+// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
+public class RulesStateTest {
+
+ @Test
+ public void equalsAndHashCode() {
+ RulesState one = new RulesState(
+ "2016a", formatVersion(1, 2), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
+ assertEqualsContract(one, one);
+
+ RulesState two = new RulesState(
+ "2016a", formatVersion(1, 2), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
+ assertEqualsContract(one, two);
+
+ RulesState differentSystemRules = new RulesState(
+ "2016b", formatVersion(1, 2), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
+ assertFalse(one.equals(differentSystemRules));
+
+ RulesState differentFormatVersion = new RulesState(
+ "2016a", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
+ assertFalse(one.equals(differentFormatVersion));
+
+ RulesState differentOperationInProgress = new RulesState(
+ "2016a", formatVersion(1, 1), true /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
+ assertFalse(one.equals(differentOperationInProgress));
+
+ RulesState differentStagedOperation = new RulesState(
+ "2016a", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNINSTALL, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
+ assertFalse(one.equals(differentStagedOperation));
+
+ RulesState differentStagedInstallVersion = new RulesState(
+ "2016a", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 4),
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
+ assertFalse(one.equals(differentStagedInstallVersion));
+
+ RulesState differentInstalled = new RulesState(
+ "2016a", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
+ RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
+ assertFalse(one.equals(differentInstalled));
+
+ RulesState differentInstalledVersion = new RulesState(
+ "2016a", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 3));
+ assertFalse(one.equals(differentInstalledVersion));
+ }
+
+ @Test
+ public void parcelable() {
+ RulesState rulesState1 = new RulesState(
+ "2016a", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016b", 2),
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 3));
+ checkParcelableRoundTrip(rulesState1);
+
+ RulesState rulesStateWithNulls = new RulesState(
+ "2016a", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
+ checkParcelableRoundTrip(rulesStateWithNulls);
+
+ RulesState rulesStateWithUnknowns = new RulesState(
+ "2016a", formatVersion(1, 1), true /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
+ checkParcelableRoundTrip(rulesStateWithNulls);
+ }
+
+ private static void checkParcelableRoundTrip(RulesState rulesState) {
+ Parcel parcel = Parcel.obtain();
+ rulesState.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+
+ RulesState newVersion = RulesState.CREATOR.createFromParcel(parcel);
+
+ assertEquals(rulesState, newVersion);
+ }
+
+ @Test
+ public void isSystemVersionOlderThan() {
+ RulesState rulesState = new RulesState(
+ "2016b", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 3));
+ assertFalse(rulesState.isSystemVersionOlderThan(rulesVersion("2016a", 1)));
+ assertFalse(rulesState.isSystemVersionOlderThan(rulesVersion("2016b", 1)));
+ assertTrue(rulesState.isSystemVersionOlderThan(rulesVersion("2016c", 1)));
+ }
+
+ @Test
+ public void isInstalledDistroOlderThan() {
+ RulesState operationInProgress = new RulesState(
+ "2016b", formatVersion(1, 1), true /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
+ RulesState.STAGED_OPERATION_UNKNOWN, null /* installedDistroRulesVersion */);
+ try {
+ operationInProgress.isInstalledDistroOlderThan(rulesVersion("2016b", 1));
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ RulesState nothingInstalled = new RulesState(
+ "2016b", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
+ try {
+ nothingInstalled.isInstalledDistroOlderThan(rulesVersion("2016b", 1));
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ DistroRulesVersion installedVersion = rulesVersion("2016b", 3);
+ RulesState rulesStateWithInstalledVersion = new RulesState(
+ "2016b", formatVersion(1, 1), false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_INSTALLED, installedVersion);
+
+ DistroRulesVersion olderRules = rulesVersion("2016a", 1);
+ assertEquals(installedVersion.isOlderThan(olderRules),
+ rulesStateWithInstalledVersion.isInstalledDistroOlderThan(olderRules));
+
+ DistroRulesVersion sameRules = rulesVersion("2016b", 1);
+ assertEquals(installedVersion.isOlderThan(sameRules),
+ rulesStateWithInstalledVersion.isInstalledDistroOlderThan(sameRules));
+
+ DistroRulesVersion newerRules = rulesVersion("2016c", 1);
+ assertEquals(installedVersion.isOlderThan(newerRules),
+ rulesStateWithInstalledVersion.isInstalledDistroOlderThan(newerRules));
+ }
+
+ private static void assertEqualsContract(RulesState one, RulesState two) {
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+
+ private static DistroRulesVersion rulesVersion(String rulesVersion, int revision) {
+ return new DistroRulesVersion(rulesVersion, revision);
+ }
+
+ private static DistroFormatVersion formatVersion(int majorVersion, int minorVersion) {
+ return new DistroFormatVersion(majorVersion, minorVersion);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
new file mode 100644
index 0000000..e7a839c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+
+import android.content.Context;
+import android.content.Intent;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+
+/**
+ * Tests for {@link RulesUpdaterContract}.
+ */
+// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
+public class RulesUpdaterContractTest {
+
+ @Test
+ public void createUpdaterIntent() throws Exception {
+ String packageName = "foobar";
+ Intent intent = RulesUpdaterContract.createUpdaterIntent(packageName);
+
+ assertEquals(RulesUpdaterContract.ACTION_TRIGGER_RULES_UPDATE_CHECK, intent.getAction());
+ assertEquals(packageName, intent.getPackage());
+ assertEquals(Intent.FLAG_INCLUDE_STOPPED_PACKAGES, intent.getFlags());
+ }
+
+ @Test
+ public void sendBroadcast() throws Exception {
+ String packageName = "foobar";
+ byte[] tokenBytes = new byte[] { 1, 2, 3, 4, 5 };
+
+ Intent expectedIntent = RulesUpdaterContract.createUpdaterIntent(packageName);
+ expectedIntent.putExtra(RulesUpdaterContract.EXTRA_CHECK_TOKEN, tokenBytes);
+
+ Context mockContext = mock(Context.class);
+
+ RulesUpdaterContract.sendBroadcast(mockContext, packageName, tokenBytes);
+
+ verify(mockContext).sendBroadcast(
+ filterEquals(expectedIntent),
+ eq(RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION));
+ }
+
+ /**
+ * Registers a mockito parameter matcher that uses {@link Intent#filterEquals(Intent)}. to
+ * check the parameter against the intent supplied.
+ */
+ private static Intent filterEquals(final Intent expected) {
+ final Matcher<Intent> m = new BaseMatcher<Intent>() {
+ @Override
+ public boolean matches(Object actual) {
+ return actual != null && expected.filterEquals((Intent) actual);
+ }
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(expected.toString());
+ }
+ };
+ return argThat(m);
+ }
+}
diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java
index aaaf55c..821ee80 100644
--- a/core/tests/coretests/src/android/os/VintfObjectTest.java
+++ b/core/tests/coretests/src/android/os/VintfObjectTest.java
@@ -26,5 +26,8 @@
// From /system/manifest.xml
assertTrue(String.join("", xmls).contains(
"<manifest version=\"1.0\" type=\"framework\">"));
+ // From /system/compatibility-matrix.xml
+ assertTrue(String.join("", xmls).contains(
+ "<compatibility-matrix version=\"1.0\" type=\"framework\">"));
}
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8d64aed..4f34317 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -110,6 +110,8 @@
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
+ Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
+ Settings.Global.BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX,
Settings.Global.BLUETOOTH_DISABLED_PROFILES,
Settings.Global.BLUETOOTH_HEADSET_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX,
@@ -394,8 +396,11 @@
Settings.Secure.ANR_SHOW_BACKGROUND,
Settings.Secure.ASSISTANT,
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+ Settings.Secure.ASSIST_GESTURE_ENABLED_KEYGUARD,
+ Settings.Secure.ASSIST_GESTURE_ENABLED_SLEEP,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+ Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
@@ -403,7 +408,6 @@
Settings.Secure.BACKUP_ENABLED,
Settings.Secure.BACKUP_PROVISIONED,
Settings.Secure.BACKUP_TRANSPORT,
- Settings.Secure.BLUETOOTH_HCI_LOG,
Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, // Candidate for backup?
Settings.Secure.CARRIER_APPS_HANDLED,
Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index f59e4fc..742fd60 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -118,7 +118,7 @@
if (isTextClassifierDisabled()) return;
String text = "Visit http://www.android.com for more information";
- String classifiedText = "http://www.android.com";
+ String classifiedText = "www.android.com";
int startIndex = text.indexOf(classifiedText);
int endIndex = startIndex + classifiedText.length();
assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES),
@@ -193,7 +193,19 @@
public boolean matches(Object o) {
if (o instanceof TextClassification) {
TextClassification result = (TextClassification) o;
- return text.equals(result.getText())
+ final boolean typeRequirementSatisfied;
+ switch (type) {
+ case TextClassifier.TYPE_URL:
+ String scheme = result.getIntent().getData().getScheme();
+ typeRequirementSatisfied = "http".equalsIgnoreCase(scheme)
+ || "https".equalsIgnoreCase(scheme);
+ break;
+ default:
+ typeRequirementSatisfied = true;
+ }
+
+ return typeRequirementSatisfied
+ && text.equals(result.getText())
&& result.getEntityCount() > 0
&& type.equals(result.getEntity(0));
// TODO: Include other properties.
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
new file mode 100644
index 0000000..9f2d129
--- /dev/null
+++ b/libs/hwui/Android.bp
@@ -0,0 +1,386 @@
+cc_defaults {
+ name: "hwui_defaults",
+ defaults: [
+ "hwui_static_deps",
+
+ //"hwui_bugreport_font_cache_usage",
+ //"hwui_compile_for_perf",
+
+ // Enables fine-grained GLES error checking
+ // If enabled, every GLES call is wrapped & error checked
+ // Has moderate overhead
+ //"hwui_enable_opengl-validation",
+ ],
+
+ cflags: [
+ "-DEGL_EGLEXT_PROTOTYPES",
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DATRACE_TAG=ATRACE_TAG_VIEW",
+ "-DLOG_TAG=\"OpenGLRenderer\"",
+ "-Wall",
+ "-Wno-unused-parameter",
+ "-Wunreachable-code",
+ "-Werror",
+ "-fvisibility=hidden",
+
+ // GCC false-positives on this warning, and since we -Werror that's
+ // a problem
+ "-Wno-free-nonheap-object",
+
+ // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
+ "-Wno-missing-braces",
+
+ // TODO: Linear blending should be enabled by default, but we are
+ // TODO: making it an opt-in while it's a work in progress
+ //"-DANDROID_ENABLE_LINEAR_BLENDING",
+ ],
+
+ include_dirs: [
+ "external/skia/include/private",
+ "external/skia/src/core",
+ "external/skia/src/effects",
+ "external/skia/src/image",
+ "external/skia/src/utils",
+ ],
+
+ product_variables: {
+ device_uses_hwc2: {
+ cflags: ["-DUSE_HWC2"],
+ },
+ },
+}
+
+cc_defaults {
+ name: "hwui_static_deps",
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libutils",
+ "libEGL",
+ "libGLESv2",
+ "libvulkan",
+ "libskia",
+ "libui",
+ "libgui",
+ "libprotobuf-cpp-full",
+ "libharfbuzz_ng",
+ "libft2",
+ "libminikin",
+ "libandroidfw",
+ "libRScpp",
+ ],
+ static_libs: [
+ "libplatformprotos",
+ ],
+}
+
+cc_defaults {
+ name: "hwui_bugreport_font_cache_usage",
+ srcs: ["font/FontCacheHistoryTracker.cpp"],
+ cflags: ["-DBUGREPORT_FONT_CACHE_USAGE"],
+}
+
+cc_defaults {
+ name: "hwui_compile_for_perf",
+ // TODO: Non-arm?
+ cflags: [
+ "-fno-omit-frame-pointer",
+ "-marm",
+ "-mapcs",
+ ],
+}
+
+cc_defaults {
+ name: "hwui_debug",
+ cflags: ["-include debug/wrap_gles.h"],
+ srcs: [
+ "debug/wrap_gles.cpp",
+ "debug/DefaultGlesDriver.cpp",
+ "debug/GlesErrorCheckWrapper.cpp",
+ "debug/GlesDriver.cpp",
+ "debug/FatalBaseDriver.cpp",
+ "debug/NullGlesDriver.cpp",
+ ],
+ include_dirs: ["frameworks/native/opengl/libs/GLES2"],
+}
+
+cc_defaults {
+ name: "hwui_enable_opengl_validation",
+ defaults: ["hwui_debug"],
+ cflags: ["-DDEBUG_OPENGL=3"],
+ srcs: ["debug/wrap_gles.cpp"],
+ include_dirs: ["frameworks/native/opengl/libs/GLES2"],
+}
+
+// ------------------------
+// library
+// ------------------------
+
+cc_defaults {
+ name: "libhwui_defaults",
+ defaults: ["hwui_defaults"],
+ srcs: [
+ "hwui/Bitmap.cpp",
+ "font/CacheTexture.cpp",
+ "font/Font.cpp",
+ "hwui/Canvas.cpp",
+ "hwui/MinikinSkia.cpp",
+ "hwui/MinikinUtils.cpp",
+ "hwui/PaintImpl.cpp",
+ "hwui/Typeface.cpp",
+ "pipeline/skia/GLFunctorDrawable.cpp",
+ "pipeline/skia/LayerDrawable.cpp",
+ "pipeline/skia/RenderNodeDrawable.cpp",
+ "pipeline/skia/ReorderBarrierDrawables.cpp",
+ "pipeline/skia/SkiaDisplayList.cpp",
+ "pipeline/skia/SkiaOpenGLPipeline.cpp",
+ "pipeline/skia/SkiaOpenGLReadback.cpp",
+ "pipeline/skia/SkiaPipeline.cpp",
+ "pipeline/skia/SkiaProfileRenderer.cpp",
+ "pipeline/skia/SkiaRecordingCanvas.cpp",
+ "pipeline/skia/SkiaVulkanPipeline.cpp",
+ "renderstate/Blend.cpp",
+ "renderstate/MeshState.cpp",
+ "renderstate/OffscreenBufferPool.cpp",
+ "renderstate/PixelBufferState.cpp",
+ "renderstate/RenderState.cpp",
+ "renderstate/Scissor.cpp",
+ "renderstate/Stencil.cpp",
+ "renderstate/TextureState.cpp",
+ "renderthread/CanvasContext.cpp",
+ "renderthread/OpenGLPipeline.cpp",
+ "renderthread/DrawFrameTask.cpp",
+ "renderthread/EglManager.cpp",
+ "renderthread/VulkanManager.cpp",
+ "renderthread/RenderProxy.cpp",
+ "renderthread/RenderTask.cpp",
+ "renderthread/RenderThread.cpp",
+ "renderthread/TimeLord.cpp",
+ "renderthread/Frame.cpp",
+ "service/GraphicsStatsService.cpp",
+ "thread/TaskManager.cpp",
+ "utils/Blur.cpp",
+ "utils/Color.cpp",
+ "utils/GLUtils.cpp",
+ "utils/LinearAllocator.cpp",
+ "utils/StringUtils.cpp",
+ "utils/TestWindowContext.cpp",
+ "utils/VectorDrawableUtils.cpp",
+ "AmbientShadow.cpp",
+ "AnimationContext.cpp",
+ "Animator.cpp",
+ "AnimatorManager.cpp",
+ "BakedOpDispatcher.cpp",
+ "BakedOpRenderer.cpp",
+ "BakedOpState.cpp",
+ "Caches.cpp",
+ "CanvasState.cpp",
+ "ClipArea.cpp",
+ "DamageAccumulator.cpp",
+ "DeferredLayerUpdater.cpp",
+ "DeviceInfo.cpp",
+ "DisplayList.cpp",
+ "Extensions.cpp",
+ "FboCache.cpp",
+ "FontRenderer.cpp",
+ "FrameBuilder.cpp",
+ "FrameInfo.cpp",
+ "FrameInfoVisualizer.cpp",
+ "GammaFontRenderer.cpp",
+ "GlLayer.cpp",
+ "GlopBuilder.cpp",
+ "GpuMemoryTracker.cpp",
+ "GradientCache.cpp",
+ "Image.cpp",
+ "Interpolator.cpp",
+ "JankTracker.cpp",
+ "Layer.cpp",
+ "LayerBuilder.cpp",
+ "LayerUpdateQueue.cpp",
+ "Matrix.cpp",
+ "OpDumper.cpp",
+ "OpenGLReadback.cpp",
+ "Patch.cpp",
+ "PatchCache.cpp",
+ "PathCache.cpp",
+ "PathParser.cpp",
+ "PathTessellator.cpp",
+ "PixelBuffer.cpp",
+ "ProfileRenderer.cpp",
+ "Program.cpp",
+ "ProgramCache.cpp",
+ "Properties.cpp",
+ "PropertyValuesAnimatorSet.cpp",
+ "PropertyValuesHolder.cpp",
+ "RecordingCanvas.cpp",
+ "RenderBufferCache.cpp",
+ "RenderNode.cpp",
+ "RenderProperties.cpp",
+ "ResourceCache.cpp",
+ "ShadowTessellator.cpp",
+ "SkiaCanvas.cpp",
+ "SkiaCanvasProxy.cpp",
+ "SkiaShader.cpp",
+ "Snapshot.cpp",
+ "SpotShadow.cpp",
+ "TessellationCache.cpp",
+ "TextDropShadowCache.cpp",
+ "Texture.cpp",
+ "TextureCache.cpp",
+ "VectorDrawable.cpp",
+ "VkLayer.cpp",
+ "protos/hwui.proto",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ },
+
+ export_include_dirs: ["."],
+}
+
+cc_library {
+ name: "libhwui",
+ defaults: ["libhwui_defaults"],
+}
+
+// ------------------------
+// static library null gpu
+// ------------------------
+
+cc_library_static {
+ name: "libhwui_static_debug",
+ defaults: [
+ "libhwui_defaults",
+ "hwui_debug",
+ ],
+ cflags: ["-DHWUI_NULL_GPU"],
+ srcs: [
+ "debug/nullegl.cpp",
+ ],
+ export_include_dirs: ["."],
+}
+
+cc_defaults {
+ name: "hwui_test_defaults",
+ defaults: ["hwui_defaults"],
+ test_suites: ["device-tests"],
+ srcs: [
+ "tests/common/scenes/*.cpp",
+ "tests/common/LeakChecker.cpp",
+ "tests/common/TestListViewSceneBase.cpp",
+ "tests/common/TestContext.cpp",
+ "tests/common/TestScene.cpp",
+ "tests/common/TestUtils.cpp",
+ ],
+}
+
+// ------------------------
+// unit tests
+// ------------------------
+
+cc_test {
+ name: "hwui_unit_tests",
+ defaults: ["hwui_test_defaults"],
+
+ static_libs: [
+ "libgmock",
+ "libhwui_static_debug",
+ ],
+ shared_libs: ["libmemunreachable"],
+ cflags: [
+ "-include debug/wrap_gles.h",
+ "-DHWUI_NULL_GPU",
+ ],
+
+ srcs: [
+ "tests/unit/main.cpp",
+ "tests/unit/BakedOpDispatcherTests.cpp",
+ "tests/unit/BakedOpRendererTests.cpp",
+ "tests/unit/BakedOpStateTests.cpp",
+ "tests/unit/BitmapTests.cpp",
+ "tests/unit/CanvasContextTests.cpp",
+ "tests/unit/CanvasStateTests.cpp",
+ "tests/unit/ClipAreaTests.cpp",
+ "tests/unit/DamageAccumulatorTests.cpp",
+ "tests/unit/DeferredLayerUpdaterTests.cpp",
+ "tests/unit/DeviceInfoTests.cpp",
+ "tests/unit/FatVectorTests.cpp",
+ "tests/unit/FontRendererTests.cpp",
+ "tests/unit/FrameBuilderTests.cpp",
+ "tests/unit/GlopBuilderTests.cpp",
+ "tests/unit/GpuMemoryTrackerTests.cpp",
+ "tests/unit/GradientCacheTests.cpp",
+ "tests/unit/GraphicsStatsServiceTests.cpp",
+ "tests/unit/LayerUpdateQueueTests.cpp",
+ "tests/unit/LeakCheckTests.cpp",
+ "tests/unit/LinearAllocatorTests.cpp",
+ "tests/unit/MatrixTests.cpp",
+ "tests/unit/MeshStateTests.cpp",
+ "tests/unit/OffscreenBufferPoolTests.cpp",
+ "tests/unit/OpDumperTests.cpp",
+ "tests/unit/PathInterpolatorTests.cpp",
+ "tests/unit/RenderNodeDrawableTests.cpp",
+ "tests/unit/RecordingCanvasTests.cpp",
+ "tests/unit/RenderNodeTests.cpp",
+ "tests/unit/RenderPropertiesTests.cpp",
+ "tests/unit/SkiaBehaviorTests.cpp",
+ "tests/unit/SkiaDisplayListTests.cpp",
+ "tests/unit/SkiaPipelineTests.cpp",
+ "tests/unit/SkiaRenderPropertiesTests.cpp",
+ "tests/unit/SkiaCanvasTests.cpp",
+ "tests/unit/SnapshotTests.cpp",
+ "tests/unit/StringUtilsTests.cpp",
+ "tests/unit/TestUtilsTests.cpp",
+ "tests/unit/TextDropShadowCacheTests.cpp",
+ "tests/unit/TextureCacheTests.cpp",
+ "tests/unit/VectorDrawableTests.cpp",
+ ],
+}
+
+// ------------------------
+// Macro-bench app
+// ------------------------
+
+cc_benchmark {
+ name: "hwuimacro",
+ defaults: ["hwui_test_defaults"],
+
+ // set to libhwui_static_debug to skip actual GL commands
+ whole_static_libs: ["libhwui"],
+ shared_libs: ["libmemunreachable"],
+
+ srcs: [
+ "tests/macrobench/TestSceneRunner.cpp",
+ "tests/macrobench/main.cpp",
+ ],
+}
+
+// ------------------------
+// Micro-bench app
+// ---------------------
+
+cc_benchmark {
+ name: "hwuimicro",
+ defaults: ["hwui_test_defaults"],
+
+ cflags: [
+ "-include debug/wrap_gles.h",
+ "-DHWUI_NULL_GPU",
+ ],
+
+ whole_static_libs: ["libhwui_static_debug"],
+ shared_libs: ["libmemunreachable"],
+
+ srcs: [
+ "tests/microbench/main.cpp",
+ "tests/microbench/DisplayListCanvasBench.cpp",
+ "tests/microbench/FontBench.cpp",
+ "tests/microbench/FrameBuilderBench.cpp",
+ "tests/microbench/LinearAllocatorBench.cpp",
+ "tests/microbench/PathParserBench.cpp",
+ "tests/microbench/RenderNodeBench.cpp",
+ "tests/microbench/ShadowBench.cpp",
+ "tests/microbench/TaskManagerBench.cpp",
+ ],
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
deleted file mode 100644
index 1e0eeb1..0000000
--- a/libs/hwui/Android.mk
+++ /dev/null
@@ -1,386 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-BUGREPORT_FONT_CACHE_USAGE := false
-
-# Enables fine-grained GLES error checking
-# If set to true, every GLES call is wrapped & error checked
-# Has moderate overhead
-HWUI_ENABLE_OPENGL_VALIDATION := false
-
-hwui_src_files := \
- hwui/Bitmap.cpp \
- font/CacheTexture.cpp \
- font/Font.cpp \
- hwui/Canvas.cpp \
- hwui/MinikinSkia.cpp \
- hwui/MinikinUtils.cpp \
- hwui/PaintImpl.cpp \
- hwui/Typeface.cpp \
- pipeline/skia/GLFunctorDrawable.cpp \
- pipeline/skia/LayerDrawable.cpp \
- pipeline/skia/RenderNodeDrawable.cpp \
- pipeline/skia/ReorderBarrierDrawables.cpp \
- pipeline/skia/SkiaDisplayList.cpp \
- pipeline/skia/SkiaOpenGLPipeline.cpp \
- pipeline/skia/SkiaOpenGLReadback.cpp \
- pipeline/skia/SkiaPipeline.cpp \
- pipeline/skia/SkiaProfileRenderer.cpp \
- pipeline/skia/SkiaRecordingCanvas.cpp \
- pipeline/skia/SkiaVulkanPipeline.cpp \
- renderstate/Blend.cpp \
- renderstate/MeshState.cpp \
- renderstate/OffscreenBufferPool.cpp \
- renderstate/PixelBufferState.cpp \
- renderstate/RenderState.cpp \
- renderstate/Scissor.cpp \
- renderstate/Stencil.cpp \
- renderstate/TextureState.cpp \
- renderthread/CanvasContext.cpp \
- renderthread/OpenGLPipeline.cpp \
- renderthread/DrawFrameTask.cpp \
- renderthread/EglManager.cpp \
- renderthread/VulkanManager.cpp \
- renderthread/RenderProxy.cpp \
- renderthread/RenderTask.cpp \
- renderthread/RenderThread.cpp \
- renderthread/TimeLord.cpp \
- renderthread/Frame.cpp \
- service/GraphicsStatsService.cpp \
- thread/TaskManager.cpp \
- utils/Blur.cpp \
- utils/Color.cpp \
- utils/GLUtils.cpp \
- utils/LinearAllocator.cpp \
- utils/StringUtils.cpp \
- utils/TestWindowContext.cpp \
- utils/VectorDrawableUtils.cpp \
- AmbientShadow.cpp \
- AnimationContext.cpp \
- Animator.cpp \
- AnimatorManager.cpp \
- BakedOpDispatcher.cpp \
- BakedOpRenderer.cpp \
- BakedOpState.cpp \
- Caches.cpp \
- CanvasState.cpp \
- ClipArea.cpp \
- DamageAccumulator.cpp \
- DeferredLayerUpdater.cpp \
- DeviceInfo.cpp \
- DisplayList.cpp \
- Extensions.cpp \
- FboCache.cpp \
- FontRenderer.cpp \
- FrameBuilder.cpp \
- FrameInfo.cpp \
- FrameInfoVisualizer.cpp \
- GammaFontRenderer.cpp \
- GlLayer.cpp \
- GlopBuilder.cpp \
- GpuMemoryTracker.cpp \
- GradientCache.cpp \
- Image.cpp \
- Interpolator.cpp \
- JankTracker.cpp \
- Layer.cpp \
- LayerBuilder.cpp \
- LayerUpdateQueue.cpp \
- Matrix.cpp \
- OpDumper.cpp \
- OpenGLReadback.cpp \
- Patch.cpp \
- PatchCache.cpp \
- PathCache.cpp \
- PathParser.cpp \
- PathTessellator.cpp \
- PixelBuffer.cpp \
- ProfileRenderer.cpp \
- Program.cpp \
- ProgramCache.cpp \
- Properties.cpp \
- PropertyValuesAnimatorSet.cpp \
- PropertyValuesHolder.cpp \
- RecordingCanvas.cpp \
- RenderBufferCache.cpp \
- RenderNode.cpp \
- RenderProperties.cpp \
- ResourceCache.cpp \
- ShadowTessellator.cpp \
- SkiaCanvas.cpp \
- SkiaCanvasProxy.cpp \
- SkiaShader.cpp \
- Snapshot.cpp \
- SpotShadow.cpp \
- TessellationCache.cpp \
- TextDropShadowCache.cpp \
- Texture.cpp \
- TextureCache.cpp \
- VectorDrawable.cpp \
- VkLayer.cpp \
- protos/hwui.proto
-
-hwui_test_common_src_files := \
- $(call all-cpp-files-under, tests/common/scenes) \
- tests/common/LeakChecker.cpp \
- tests/common/TestListViewSceneBase.cpp \
- tests/common/TestContext.cpp \
- tests/common/TestScene.cpp \
- tests/common/TestUtils.cpp
-
-hwui_debug_common_src_files := \
- debug/wrap_gles.cpp \
- debug/DefaultGlesDriver.cpp \
- debug/GlesErrorCheckWrapper.cpp \
- debug/GlesDriver.cpp \
- debug/FatalBaseDriver.cpp \
- debug/NullGlesDriver.cpp
-
-hwui_cflags := \
- -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES \
- -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
- -Wall -Wno-unused-parameter -Wunreachable-code -Werror
-
-ifeq ($(TARGET_USES_HWC2),true)
- hwui_cflags += -DUSE_HWC2
-endif
-
-# TODO: Linear blending should be enabled by default, but we are
-# TODO: making it an opt-in while it's a work in progress
-# TODO: The final test should be:
-# TODO: ifneq ($(TARGET_ENABLE_LINEAR_BLENDING),false)
-ifeq ($(TARGET_ENABLE_LINEAR_BLENDING),true)
- hwui_cflags += -DANDROID_ENABLE_LINEAR_BLENDING
-endif
-
-# GCC false-positives on this warning, and since we -Werror that's
-# a problem
-hwui_cflags += -Wno-free-nonheap-object
-
-# clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
-hwui_cflags += -Wno-missing-braces
-
-ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE))
- hwui_src_files += \
- font/FontCacheHistoryTracker.cpp
- hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE
-endif
-
-ifndef HWUI_COMPILE_SYMBOLS
- hwui_cflags += -fvisibility=hidden
-endif
-
-ifdef HWUI_COMPILE_FOR_PERF
- # TODO: Non-arm?
- hwui_cflags += -fno-omit-frame-pointer -marm -mapcs
-endif
-
-# This has to be lazy-resolved because it depends on the LOCAL_MODULE_CLASS
-# which varies depending on what is being built
-define hwui_proto_include
-$(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
-endef
-
-hwui_c_includes += \
- external/skia/include/private \
- external/skia/src/core \
- external/skia/src/effects \
- external/skia/src/image \
- external/skia/src/utils \
- external/icu/icu4c/source/common \
- external/harfbuzz_ng/src \
- external/freetype/include
-
-# enable RENDERSCRIPT
-hwui_c_includes += \
- $(call intermediates-dir-for,STATIC_LIBRARIES,TARGET,) \
- frameworks/rs/cpp \
- frameworks/rs
-
-# ------------------------
-# static library
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := STATIC_LIBRARIES
-LOCAL_MODULE := libhwui_static
-LOCAL_CFLAGS := $(hwui_cflags)
-LOCAL_SRC_FILES := $(hwui_src_files)
-
-ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION))
- LOCAL_CFLAGS += -include debug/wrap_gles.h
- LOCAL_CFLAGS += -DDEBUG_OPENGL=3
- LOCAL_SRC_FILES += $(hwui_debug_common_src_files)
-endif
-
-LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
- $(LOCAL_PATH) \
- $(call hwui_proto_include)
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_STATIC_LIBRARY)
-
-# ------------------------
-# static library null gpu
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := STATIC_LIBRARIES
-LOCAL_MODULE := libhwui_static_debug
-LOCAL_CFLAGS := \
- $(hwui_cflags) \
- -include debug/wrap_gles.h \
- -DHWUI_NULL_GPU
-LOCAL_SRC_FILES := \
- $(hwui_src_files) \
- $(hwui_debug_common_src_files) \
- debug/nullegl.cpp
-LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
- $(LOCAL_PATH) \
- $(call hwui_proto_include)
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_STATIC_LIBRARY)
-
-# ------------------------
-# shared library
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_MODULE := libhwui
-LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_SHARED_LIBRARY)
-
-# ------------------------
-# unit tests
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := hwui_unit_tests
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_LIBRARIES := libgmock libhwui_static_debug
-LOCAL_SHARED_LIBRARIES := libmemunreachable
-LOCAL_CFLAGS := \
- $(hwui_cflags) \
- -include debug/wrap_gles.h \
- -DHWUI_NULL_GPU
-LOCAL_C_INCLUDES := $(hwui_c_includes)
-
-LOCAL_SRC_FILES += \
- $(hwui_test_common_src_files) \
- tests/unit/main.cpp \
- tests/unit/BakedOpDispatcherTests.cpp \
- tests/unit/BakedOpRendererTests.cpp \
- tests/unit/BakedOpStateTests.cpp \
- tests/unit/BitmapTests.cpp \
- tests/unit/CanvasContextTests.cpp \
- tests/unit/CanvasStateTests.cpp \
- tests/unit/ClipAreaTests.cpp \
- tests/unit/DamageAccumulatorTests.cpp \
- tests/unit/DeferredLayerUpdaterTests.cpp \
- tests/unit/DeviceInfoTests.cpp \
- tests/unit/FatVectorTests.cpp \
- tests/unit/FontRendererTests.cpp \
- tests/unit/FrameBuilderTests.cpp \
- tests/unit/GlopBuilderTests.cpp \
- tests/unit/GpuMemoryTrackerTests.cpp \
- tests/unit/GradientCacheTests.cpp \
- tests/unit/GraphicsStatsServiceTests.cpp \
- tests/unit/LayerUpdateQueueTests.cpp \
- tests/unit/LeakCheckTests.cpp \
- tests/unit/LinearAllocatorTests.cpp \
- tests/unit/MatrixTests.cpp \
- tests/unit/MeshStateTests.cpp \
- tests/unit/OffscreenBufferPoolTests.cpp \
- tests/unit/OpDumperTests.cpp \
- tests/unit/PathInterpolatorTests.cpp \
- tests/unit/RenderNodeDrawableTests.cpp \
- tests/unit/RecordingCanvasTests.cpp \
- tests/unit/RenderNodeTests.cpp \
- tests/unit/RenderPropertiesTests.cpp \
- tests/unit/SkiaBehaviorTests.cpp \
- tests/unit/SkiaDisplayListTests.cpp \
- tests/unit/SkiaPipelineTests.cpp \
- tests/unit/SkiaRenderPropertiesTests.cpp \
- tests/unit/SkiaCanvasTests.cpp \
- tests/unit/SnapshotTests.cpp \
- tests/unit/StringUtilsTests.cpp \
- tests/unit/TestUtilsTests.cpp \
- tests/unit/TextDropShadowCacheTests.cpp \
- tests/unit/TextureCacheTests.cpp \
- tests/unit/VectorDrawableTests.cpp \
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_NATIVE_TEST)
-
-# ------------------------
-# Macro-bench app
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
-LOCAL_MODULE:= hwuimacro
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-LOCAL_MULTILIB := both
-LOCAL_CFLAGS := $(hwui_cflags)
-LOCAL_C_INCLUDES := $(hwui_c_includes)
-
-# set to libhwui_static_debug to skip actual GL commands
-LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-LOCAL_SHARED_LIBRARIES := libmemunreachable
-
-LOCAL_SRC_FILES += \
- $(hwui_test_common_src_files) \
- tests/macrobench/TestSceneRunner.cpp \
- tests/macrobench/main.cpp
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_NATIVE_BENCHMARK)
-
-# ------------------------
-# Micro-bench app
-# ---------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= hwuimicro
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS := \
- $(hwui_cflags) \
- -include debug/wrap_gles.h \
- -DHWUI_NULL_GPU
-
-LOCAL_C_INCLUDES := $(hwui_c_includes)
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_debug
-LOCAL_SHARED_LIBRARIES := libmemunreachable
-
-LOCAL_SRC_FILES += \
- $(hwui_test_common_src_files) \
- tests/microbench/main.cpp \
- tests/microbench/DisplayListCanvasBench.cpp \
- tests/microbench/FontBench.cpp \
- tests/microbench/FrameBuilderBench.cpp \
- tests/microbench/LinearAllocatorBench.cpp \
- tests/microbench/PathParserBench.cpp \
- tests/microbench/RenderNodeBench.cpp \
- tests/microbench/ShadowBench.cpp \
- tests/microbench/TaskManagerBench.cpp
-
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_NATIVE_BENCHMARK)
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 929d1606..b113626 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -317,7 +317,17 @@
}
void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
- reconfigure(info().makeColorSpace(std::move(colorSpace)), rowBytes(), sk_ref_sp(colorTable()));
+ mInfo = mInfo.makeColorSpace(std::move(colorSpace));
+}
+
+static SkImageInfo validateAlpha(const SkImageInfo& info) {
+ // Need to validate the alpha type to filter against the color type
+ // to prevent things like a non-opaque RGB565 bitmap
+ SkAlphaType alphaType;
+ LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
+ info.colorType(), info.alphaType(), &alphaType),
+ "Failed to validate alpha type!");
+ return info.makeAlphaType(alphaType);
}
void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, sk_sp<SkColorTable> ctable) {
@@ -325,19 +335,14 @@
ctable = nullptr;
}
- // Need to validate the alpha type to filter against the color type
- // to prevent things like a non-opaque RGB565 bitmap
- SkAlphaType alphaType;
- LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
- newInfo.colorType(), newInfo.alphaType(), &alphaType),
- "Failed to validate alpha type!");
+ mInfo = validateAlpha(newInfo);
// Dirty hack is dirty
// TODO: Figure something out here, Skia's current design makes this
// really hard to work with. Skia really, really wants immutable objects,
// but with the nested-ref-count hackery going on that's just not
// feasible without going insane trying to figure it out
- this->android_only_reset(newInfo.makeAlphaType(alphaType), rowBytes, std::move(ctable));
+ this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes, std::move(ctable));
}
static sk_sp<SkColorTable> sanitize(const SkImageInfo& info, sk_sp<SkColorTable> ctable) {
@@ -347,8 +352,11 @@
}
return nullptr; // drop the ctable if we're not indexed
}
-Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable)
- : SkPixelRef(info, address, rowBytes, sanitize(info, std::move(ctable)))
+Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes,
+ sk_sp<SkColorTable> ctable)
+ : SkPixelRef(info.width(), info.height(), address, rowBytes,
+ sanitize(info, std::move(ctable)))
+ , mInfo(validateAlpha(info))
, mPixelStorageType(PixelStorageType::Heap) {
mPixelStorage.heap.address = address;
mPixelStorage.heap.size = size;
@@ -356,7 +364,9 @@
Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable)
- : SkPixelRef(info, address, rowBytes, sanitize(info, std::move(ctable)))
+ : SkPixelRef(info.width(), info.height(), address, rowBytes,
+ sanitize(info, std::move(ctable)))
+ , mInfo(validateAlpha(info))
, mPixelStorageType(PixelStorageType::External) {
mPixelStorage.external.address = address;
mPixelStorage.external.context = context;
@@ -365,7 +375,9 @@
Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable)
- : SkPixelRef(info, address, rowBytes, sanitize(info, std::move(ctable)))
+ : SkPixelRef(info.width(), info.height(), address, rowBytes,
+ sanitize(info, std::move(ctable)))
+ , mInfo(validateAlpha(info))
, mPixelStorageType(PixelStorageType::Ashmem) {
mPixelStorage.ashmem.address = address;
mPixelStorage.ashmem.fd = fd;
@@ -373,9 +385,10 @@
}
Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
- : SkPixelRef(info, nullptr,
+ : SkPixelRef(info.width(), info.height(), nullptr,
bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride(),
nullptr)
+ , mInfo(validateAlpha(info))
, mPixelStorageType(PixelStorageType::Hardware) {
mPixelStorage.hardware.buffer = buffer;
buffer->incStrong(buffer);
@@ -428,10 +441,6 @@
}
}
-size_t Bitmap::getAllocatedSizeInBytes() const {
- return info().getSafeSize(this->rowBytes());
-}
-
int Bitmap::getAshmemFd() const {
switch (mPixelStorageType) {
case PixelStorageType::Ashmem:
@@ -459,7 +468,7 @@
return;
}
- changeAlphaType(alphaType);
+ mInfo = mInfo.makeAlphaType(alphaType);
}
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
@@ -470,19 +479,19 @@
uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
return;
}
- outBitmap->setInfo(info(), rowBytes());
+ outBitmap->setInfo(mInfo, rowBytes());
outBitmap->setPixelRef(sk_ref_sp(this), 0, 0);
}
void Bitmap::getSkBitmapForShaders(SkBitmap* outBitmap) {
- outBitmap->setInfo(info(), rowBytes());
+ outBitmap->setInfo(mInfo, rowBytes());
outBitmap->setPixelRef(sk_ref_sp(this), 0, 0);
outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
}
void Bitmap::getBounds(SkRect* bounds) const {
SkASSERT(bounds);
- bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height()));
+ bounds->set(0, 0, SkIntToScalar(width()), SkIntToScalar(height()));
}
GraphicBuffer* Bitmap::graphicBuffer() {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 701b9db..9a76715 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -67,11 +67,8 @@
Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
size_t rowBytes, sk_sp<SkColorTable> ctable);
- int width() const { return info().width(); }
- int height() const { return info().height(); }
-
int rowBytesAsPixels() const {
- return rowBytes() >> info().shiftPerPixel();
+ return rowBytes() >> SkColorTypeShiftPerPixel(mInfo.colorType());
}
void reconfigure(const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable);
@@ -91,8 +88,12 @@
void setHasHardwareMipMap(bool hasMipMap);
bool hasHardwareMipMap() const;
- bool isOpaque() const {return info().isOpaque(); }
- SkColorType colorType() const { return info().colorType(); }
+ bool isOpaque() const { return mInfo.isOpaque(); }
+ SkColorType colorType() const { return mInfo.colorType(); }
+ const SkImageInfo& info() const {
+ return mInfo;
+ }
+
void getBounds(SkRect* bounds) const;
bool readyToDraw() const {
@@ -104,13 +105,13 @@
}
GraphicBuffer* graphicBuffer();
-protected:
- virtual size_t getAllocatedSizeInBytes() const override;
private:
Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
virtual ~Bitmap();
void* getStorage() const;
+ SkImageInfo mInfo;
+
const PixelStorageType mPixelStorageType;
bool mHasHardwareMipMap = false;
@@ -136,4 +137,4 @@
} mPixelStorage;
};
-} //namespace android
\ No newline at end of file
+} //namespace android
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
deleted file mode 100644
index 8826cfc..0000000
--- a/libs/hwui/hwui_static_deps.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-###############################################################################
-#
-#
-# This file contains the shared and static dependencies needed by any target
-# that attempts to statically link HWUI (i.e. libhwui_static build target). This
-# file should be included by any target that lists libhwui_static as a
-# dependency.
-#
-# This is a workaround for the fact that the build system does not add these
-# transitive dependencies when it attempts to link libhwui_static into another
-# library.
-#
-###############################################################################
-
-LOCAL_SHARED_LIBRARIES += \
- liblog \
- libcutils \
- libutils \
- libEGL \
- libGLESv2 \
- libvulkan \
- libskia \
- libui \
- libgui \
- libprotobuf-cpp-full \
- libharfbuzz_ng \
- libft2 \
- libminikin \
- libandroidfw \
- libRScpp
-
-LOCAL_STATIC_LIBRARIES += \
- libplatformprotos
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index bd798e8..fcdd814 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -819,7 +819,7 @@
EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
}
-OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -2321,7 +2321,7 @@
EXPECT_EQ(1, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
/* R is backward projected on B
A
/ \
@@ -2351,7 +2351,7 @@
EXPECT_EQ(3, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) {
/* R is backward projected on E
A
/ | \
@@ -2383,7 +2383,7 @@
EXPECT_EQ(4, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) {
/* R is backward projected without receiver
A
/ \
@@ -2412,7 +2412,7 @@
EXPECT_EQ(2, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) {
/* R is backward projected on C
A
/ \
@@ -2441,7 +2441,7 @@
EXPECT_EQ(3, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[](RenderProperties& props, RecordingCanvas& canvas) {
drawOrderedNode(&canvas, 0, nullptr); //nodeB
@@ -2464,7 +2464,7 @@
EXPECT_EQ(2, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) {
//TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a
//bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical
//tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling
@@ -2497,7 +2497,7 @@
EXPECT_EQ(3, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) {
/* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
A
|
@@ -2530,7 +2530,7 @@
EXPECT_EQ(3, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
/* R is backward projected on B
A
|
@@ -2562,7 +2562,7 @@
EXPECT_EQ(3, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) {
/* B and G are receivables, R is backward projected
A
/ \
@@ -2595,7 +2595,7 @@
EXPECT_EQ(4, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
/* B and G are receivables, G is backward projected
A
/ \
@@ -2628,7 +2628,7 @@
EXPECT_EQ(4, renderer.getIndex());
}
-OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
/* B and G are receivables, R is backward projected
A
/ \
diff --git a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
index cfe1134..f6f7337 100644
--- a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
+++ b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
@@ -50,7 +50,11 @@
// No code left untested
TEST(GraphicsStats, findRootPath) {
+#ifdef __LP64__
+ std::string expected = "/data/nativetest64/hwui_unit_tests";
+#else
std::string expected = "/data/nativetest/hwui_unit_tests";
+#endif
EXPECT_EQ(expected, findRootPath());
}
@@ -156,4 +160,4 @@
EXPECT_EQ(expectedCount, loadedProto.histogram().Get(i).frame_count());
EXPECT_EQ(expectedBucket, loadedProto.histogram().Get(i).render_millis());
}
-}
\ No newline at end of file
+}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index 3831cf7..dbfeefd 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -104,7 +104,6 @@
case ObexHelper.OBEX_OPCODE_DISCONNECT:
handleDisconnectRequest();
- done = true;
break;
case ObexHelper.OBEX_OPCODE_GET:
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 9329d2a..8e12525 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -49,7 +49,7 @@
<string name="print_options_collapsed" msgid="7455930445670414332">"Options d\'impression réduites"</string>
<string name="search" msgid="5421724265322228497">"Rechercher"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Toutes les imprimantes"</string>
- <string name="add_print_service_label" msgid="5356702546188981940">"Ajouter le service"</string>
+ <string name="add_print_service_label" msgid="5356702546188981940">"Ajouter un service"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Champ de recherche affiché"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Champ de recherche masqué"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Ajouter une imprimante"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index fcbb89d..70c6e47 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -203,7 +203,7 @@
<string name="allow_mock_location" msgid="2787962564578664888">"Dozvoli lažne lokacije"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Dozvoli lažne lokacije"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Omogući proveru atributa za pregled"</string>
- <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Neka podaci za mobilne uređaje uvek budu aktivni, čak i kada je Wi‑Fi aktivan (radi brze promene mreže)."</string>
+ <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Neka mobilni podaci uvek budu aktivni, čak i kada je Wi‑Fi aktivan (radi brze promene mreže)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Dozvoli otklanjanje USB grešaka?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Otklanjanje USB grešaka namenjeno je samo za svrhe programiranja. Koristite ga za kopiranje podataka sa računara na uređaj i obrnuto, instaliranje aplikacija na uređaju bez obaveštenja i čitanje podataka iz evidencije."</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"Želite li da opozovete pristup otklanjanju USB grešaka sa svih računara koje ste prethodno odobrili?"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 5891473..f851581 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -93,7 +93,7 @@
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Applications et utilisateurs supprimés"</string>
<string name="tether_settings_title_usb" msgid="6688416425801386511">"Partage de connexion par USB"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string>
- <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Via Bluetooth"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Par Bluetooth"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Partage de connexion"</string>
<string name="tether_settings_title_all" msgid="8356136101061143841">"Partage de connexion"</string>
<string name="managed_user_title" msgid="8109605045406748842">"Toutes les applis profess."</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 831515a..155f1c5 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -33,7 +33,7 @@
<string name="wifi_no_internet" msgid="3880396223819116454">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Enregistré par <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"Connecté automatiquement via %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Connecté automatiquement via un fournisseur d\'avis sur le réseau"</string>
+ <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté via %1$s"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponible via %1$s"</string>
<string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connecté, aucun accès à Internet"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index e64e007..182d819 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -203,7 +203,7 @@
<string name="allow_mock_location" msgid="2787962564578664888">"אפשר מיקומים מדומים"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"אפשר מיקומים מדומים"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"אפשר בדיקת תכונת תצוגה"</string>
- <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"השאר את הנתונים לנייד פעילים תמיד, גם כש-Wi‑Fi פעיל (למעבר מהיר בין רשתות)."</string>
+ <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"השאר את חבילת הגלישה פעילה תמיד, גם כש-Wi‑Fi פעיל (למעבר מהיר בין רשתות)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"לאפשר ניפוי באגים של USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"ניפוי באגים באמצעות USB מיועד למטרות פיתוח בלבד. השתמש בו להעתקת נתונים בין המחשב והמכשיר שלך, להתקנת אפליקציות במכשיר ללא התראה ולקריאת נתוני יומן."</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"האם לבטל את הגישה לניפוי ב-USB מכל המחשבים שהענקת להם בעבר הרשאה?"</string>
@@ -253,7 +253,7 @@
<string name="usb_audio_disable_routing" msgid="8114498436003102671">"השבת ניתוב אודיו ב-USB"</string>
<string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"השבת ניתוב אוטומטי אל התקני אודיו חיצוניים ב-USB"</string>
<string name="debug_layout" msgid="5981361776594526155">"הצג את גבולות הפריסה"</string>
- <string name="debug_layout_summary" msgid="2001775315258637682">"הצג גבולות קליפ, שוליים וכו\'"</string>
+ <string name="debug_layout_summary" msgid="2001775315258637682">"הצג גבולות אזור, שוליים וכדומה"</string>
<string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"אלץ כיוון פריסה מימין לשמאל"</string>
<string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"אלץ כיוון פריסת מסך מימין לשמאל עבור כל השפות בכל המקומות"</string>
<string name="force_hw_ui" msgid="6426383462520888732">"אלץ עיבוד ב-GPU"</string>
@@ -278,7 +278,7 @@
<string name="force_resizable_activities_summary" msgid="6667493494706124459">"אפשר יכולת קביעת גודל של כל הפעילויות לריבוי חלונות, ללא קשר לערך המניפסט."</string>
<string name="enable_freeform_support" msgid="1461893351278940416">"הפעל את האפשרות לשנות את הגודל והמיקום של החלונות"</string>
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"הפעל תמיכה בתכונה הניסיונית של שינוי הגודל והמיקום של החלונות."</string>
- <string name="local_backup_password_title" msgid="3860471654439418822">"סיסמת גיבוי מקומי"</string>
+ <string name="local_backup_password_title" msgid="3860471654439418822">"סיסמת גיבוי שולחן העבודה"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"גיבויים מלאים בשולחן העבודה אינם מוגנים כעת"</string>
<string name="local_backup_password_summary_change" msgid="5376206246809190364">"הקש כדי לשנות או להסיר את הסיסמה לגיבויים מלאים בשולחן העבודה"</string>
<string name="local_backup_password_toast_success" msgid="582016086228434290">"הוגדרה סיסמת גיבוי חדשה"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 97830f0..492f71d 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -33,7 +33,7 @@
<string name="wifi_no_internet" msgid="3880396223819116454">"Интернетке туташпай турат"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s аркылуу автоматтык түрдө туташты"</string>
- <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Тармак рейтингинин провайдери аркылуу автоматтык түрдө туташтырылды"</string>
+ <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Тармактардын рейтингинин автору аркылуу автоматтык түрдө туташты"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s аркылуу жеткиликтүү"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s аркылуу жеткиликтүү"</string>
<string name="wifi_connected_no_internet" msgid="3149853966840874992">"Туташып турат, Интернет жок"</string>
@@ -316,7 +316,7 @@
<string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"Протаномалия (кызыл-жашыл)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"Тританомалия (көк-сары)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Түсүн тууралоо"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бул сынамык мүмкүнчүлүк болгондуктан, иштин майнаптуулугуна таасир этиши мүмкүн."</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бул сынамык мүмкүнчүлүк болгондуктан, түзмөктүн иштешине таасир этиши мүмкүн."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
<string name="power_remaining_duration_only" msgid="845431008899029842">"Батарея түгөнгөнгө чейин калган убакыт: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Батарея толгонго чейин калган убакыт: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index c11fa9f..52840e3 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -165,7 +165,7 @@
<string name="confirm_enable_oem_unlock_title" msgid="4802157344812385674">"OEM түгжээ тайлагчийг зөвшөөрөх үү?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"АНХААР: Энэ тохиргоо асаалттай байгаа үед тухайн төхөөрөмжийн хамгаалалтын функцүүд ажиллахгүй."</string>
<string name="mock_location_app" msgid="7966220972812881854">"Хуурамч байршлын апп сонгох"</string>
- <string name="mock_location_app_not_set" msgid="809543285495344223">"Хуурамч байршлын апп-ыг тохируулаагүй байна"</string>
+ <string name="mock_location_app_not_set" msgid="809543285495344223">"Хуурамч байршлын аппыг тохируулаагүй байна"</string>
<string name="mock_location_app_set" msgid="8966420655295102685">"Хуурамч байршлын апп: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="7044075693643009662">"Сүлжээ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Утасгүй дэлгэцийн сертификат"</string>
@@ -233,7 +233,7 @@
<string name="media_category" msgid="4388305075496848353">"Медиа"</string>
<string name="debug_monitoring_category" msgid="7640508148375798343">"Мониторинг"</string>
<string name="strict_mode" msgid="1938795874357830695">"Хатуу горимыг идэвхжүүлсэн"</string>
- <string name="strict_mode_summary" msgid="142834318897332338">"Апп-ууд үндсэн хэлхээс дээр удаан хугацаанд үйлдлүүд хийх үед дэлгэцийг анивчуулах"</string>
+ <string name="strict_mode_summary" msgid="142834318897332338">"Аппууд үндсэн хэлхээс дээр удаан хугацаанд үйлдлүүд хийх үед дэлгэцийг анивчуулах"</string>
<string name="pointer_location" msgid="6084434787496938001">"Чиглүүлэгчийн байршил"</string>
<string name="pointer_location_summary" msgid="840819275172753713">"Дэлгэцийн давхаргаар одоогийн хүрэлтийн өгөгдлийг харуулж байна"</string>
<string name="show_touches" msgid="2642976305235070316">"Товшилтыг харуулах"</string>
@@ -272,7 +272,7 @@
<string name="app_process_limit_title" msgid="4280600650253107163">"Далд процессын хязгаар"</string>
<string name="show_all_anrs" msgid="28462979638729082">"Бүх ANRs харуулах"</string>
<string name="show_all_anrs_summary" msgid="641908614413544127">"Далд апп-уудад Апп Хариу Өгөхгүй байна гэснийг харуулах"</string>
- <string name="force_allow_on_external" msgid="3215759785081916381">"Апп-ыг гадаад санах ойд хадгалахыг зөвшөөрөх"</string>
+ <string name="force_allow_on_external" msgid="3215759785081916381">"Аппыг гадаад санах ойд хадгалахыг зөвшөөрөх"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Манифест утгыг нь үл хамааран дурын апп-г гадаад санах ойд бичих боломжтой болгодог"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"Үйл ажиллагааны хэмжээг өөрчилж болохуйц болгох"</string>
<string name="force_resizable_activities_summary" msgid="6667493494706124459">"Тодорхойлогч файлын утгыг үл хамааран, бүх үйл ажиллагааны хэмжээг олон цонхонд өөрчилж болохуйц болгоно уу."</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index ac7d7de..1e55e15 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -167,7 +167,7 @@
<string name="mock_location_app" msgid="7966220972812881854">"ਮੌਕ ਸਥਾਨ ਐਪ ਚੁਣੋ"</string>
<string name="mock_location_app_not_set" msgid="809543285495344223">"ਕੋਈ ਵੀ ਮੌਕ ਸਥਾਨ ਐਪ ਸੈੱਟ ਨਹੀਂ ਕੀਤੀ ਗਈ"</string>
<string name="mock_location_app_set" msgid="8966420655295102685">"ਮੌਕ ਸਥਾਨ ਐਪ: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="debug_networking_category" msgid="7044075693643009662">"ਨੈਟਵਰਕਿੰਗ"</string>
+ <string name="debug_networking_category" msgid="7044075693643009662">"ਨੈੱਟਵਰਕਿੰਗ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ਵਾਇਰਲੈਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi ਵਰਬੋਸ ਲੌਗਿੰਗ ਸਮਰੱਥ ਬਣਾਓ"</string>
<string name="wifi_aggressive_handover" msgid="5309131983693661320">"ਆਕਰਮਣਸ਼ੀਲ Wi‑Fi ਤੋਂ ਮੋਬਾਈਲ ਹੈਂਡਓਵਰ"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index a8f5566..75b766b 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -31,7 +31,7 @@
<string name="wifi_not_in_range" msgid="1136191511238508967">"Mimo dosah"</string>
<string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nedôjde k automatickému pripojeniu"</string>
<string name="wifi_no_internet" msgid="3880396223819116454">"Žiadny prístup k internetu"</string>
- <string name="saved_network" msgid="4352716707126620811">"Uložil(a) <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="saved_network" msgid="4352716707126620811">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"Automaticky pripojené prostredníctvom %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Pripojené prostredníctvom %1$s"</string>
@@ -91,7 +91,7 @@
<string name="process_kernel_label" msgid="3916858646836739323">"OS Android"</string>
<string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Odstránené aplikácie"</string>
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Odstránené aplikácie a používatelia"</string>
- <string name="tether_settings_title_usb" msgid="6688416425801386511">"Zdieľané pripojenie cez USB"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Pripojenie cez USB"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosný prístupový bod"</string>
<string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Pripojenie cez Bluetooth"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Zdieľané pripojenie"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 3992dbe..15ba747 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -203,7 +203,7 @@
<string name="allow_mock_location" msgid="2787962564578664888">"Дозволи лажне локације"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Дозволи лажне локације"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Омогући проверу атрибута за преглед"</string>
- <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Нека подаци за мобилне уређаје увек буду активни, чак и када је Wi‑Fi активан (ради брзе промене мреже)."</string>
+ <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Нека мобилни подаци увек буду активни, чак и када је Wi‑Fi активан (ради брзе промене мреже)."</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Дозволи отклањање USB грешака?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Отклањање USB грешака намењено је само за сврхе програмирања. Користите га за копирање података са рачунара на уређај и обрнуто, инсталирање апликација на уређају без обавештења и читање података из евиденције."</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"Желите ли да опозовете приступ отклањању USB грешака са свих рачунара које сте претходно одобрили?"</string>
diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index cc7ef9b..1d4e1e9 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -22,7 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wifi_status">
<item msgid="1922181315419294640"></item>
- <item msgid="8934131797783724664">"Tekshirib chiqilmoqda…"</item>
+ <item msgid="8934131797783724664">"Qidiruv…"</item>
<item msgid="8513729475867537913">"Ulanmoqda…"</item>
<item msgid="515055375277271756">"Tasdiqdan o‘tilmoqda…"</item>
<item msgid="1943354004029184381">"IP manzil o‘zlashtirilmoqda…"</item>
@@ -36,7 +36,7 @@
</string-array>
<string-array name="wifi_status_with_ssid">
<item msgid="7714855332363650812"></item>
- <item msgid="8878186979715711006">"Tekshirilmoqda…"</item>
+ <item msgid="8878186979715711006">"Qidiruv…"</item>
<item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> tarmog‘iga ulanilmoqda…"</item>
<item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> bilan aloqa o‘rnatilyapti…"</item>
<item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> IP manzil beryapti…"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 43d75b3b..ac7cd4e 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -31,7 +31,7 @@
<string name="wifi_not_in_range" msgid="1136191511238508967">"不在范围内"</string>
<string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"无法自动连接"</string>
<string name="wifi_no_internet" msgid="3880396223819116454">"无法连接到互联网"</string>
- <string name="saved_network" msgid="4352716707126620811">"已通过<xliff:g id="NAME">%1$s</xliff:g>保存"</string>
+ <string name="saved_network" msgid="4352716707126620811">"由<xliff:g id="NAME">%1$s</xliff:g>保存"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"已通过%1$s自动连接"</string>
<string name="connected_via_network_scorer_default" msgid="7867260222020343104">"已自动连接(通过网络评分服务提供方)"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"已通过%1$s连接"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 5a178a5..eb513e1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -998,7 +998,8 @@
if (mRssi != info.getRssi()) {
mRssi = info.getRssi();
updated = true;
- } else if (mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) {
+ } else if (mNetworkInfo != null && networkInfo != null
+ && mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) {
updated = true;
}
mInfo = info;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 646b6ba..be15e65 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -124,7 +124,8 @@
*/
private final Object mLock = new Object();
- //visible to both worker and main thread. Guarded by #mInternalAccessPoints
+ //visible to both worker and main thread.
+ @GuardedBy("mLock")
private final AccessPointListenerAdapter mAccessPointListenerAdapter
= new AccessPointListenerAdapter();
@@ -1005,12 +1006,13 @@
if (DBG) {
Log.d(TAG, "Starting to copy AP items on the MainHandler");
}
- if (notifyListeners) {
- notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone();
- }
-
- mAccessPointListenerAdapter.mPendingNotifications.clear();
synchronized (mLock) {
+ if (notifyListeners) {
+ notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone();
+ }
+
+ mAccessPointListenerAdapter.mPendingNotifications.clear();
+
for (AccessPoint internalAccessPoint : mInternalAccessPoints) {
AccessPoint accessPoint = oldAccessPoints.get(internalAccessPoint.mId);
if (accessPoint == null) {
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 f519a90..d4ce40c 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
@@ -465,9 +465,9 @@
private void updateScoresAndWaitForAccessPointsChangedCallback() throws InterruptedException {
// Updating scores can happen together or one after the other, so the latch countdown is set
// to 2.
- mAccessPointsChangedLatch = new CountDownLatch(3);
+ mAccessPointsChangedLatch = new CountDownLatch(2);
updateScores();
- assertTrue("onAccessPointChanged was not called three times",
+ assertTrue("onAccessPointChanged was not called twice",
mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 885573e..f475361 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -696,6 +696,12 @@
Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
GlobalSettingsProto.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX);
dumpSetting(s, p,
+ Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
+ GlobalSettingsProto.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX);
+ dumpSetting(s, p,
+ Settings.Global.BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX,
+ GlobalSettingsProto.BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX);
+ dumpSetting(s, p,
Settings.Global.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX,
GlobalSettingsProto.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX);
dumpSetting(s, p,
@@ -979,9 +985,6 @@
Settings.Secure.AUTOFILL_SERVICE,
SecureSettingsProto.AUTOFILL_SERVICE);
dumpSetting(s, p,
- Settings.Secure.BLUETOOTH_HCI_LOG,
- SecureSettingsProto.BLUETOOTH_HCI_LOG);
- dumpSetting(s, p,
Settings.Secure.USER_SETUP_COMPLETE,
SecureSettingsProto.USER_SETUP_COMPLETE);
dumpSetting(s, p,
diff --git a/packages/Shell/res/values-hy/strings.xml b/packages/Shell/res/values-hy/strings.xml
index 5d7f294..cd57e76 100644
--- a/packages/Shell/res/values-hy/strings.xml
+++ b/packages/Shell/res/values-hy/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="3701846017049540910">"Խեցի"</string>
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Վրիպակների հաշվետվություններ"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"<xliff:g id="ID">#%d</xliff:g> վրիպակի զեկույցը ստեղծվում է"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"<xliff:g id="ID">#%d</xliff:g> վրիպակի զեկույցը գրանցվեց"</string>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index dc86065..5c5ba816 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -26,8 +26,8 @@
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Pilih untuk membagikan laporan bug Anda"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Ketuk untuk membagikan laporan bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Pilih untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa tangkapan layar atau menunggu tangkapan layar selesai"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa tangkapan layar atau menunggu tangkapan layar selesai"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Laporan bug berisi data dari berbagai file log sistem, yang mungkin mencakup data yang dianggap sensitif (seperti data penggunaan aplikasi dan lokasi). Hanya bagikan laporan bug dengan aplikasi dan orang yang Anda percaya."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Jangan tampilkan lagi"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan bug"</string>
@@ -35,9 +35,9 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Tidak dapat menambahkan detail laporan bug ke file zip"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"tanpa nama"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Detail"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Tangkapan layar"</string>
- <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Tangkapan layar berhasil diambil."</string>
- <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Tangkapan layar tidak dapat diambil."</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Screenshot"</string>
+ <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Screenshot berhasil diambil."</string>
+ <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Screenshot tidak dapat diambil."</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Detail laporan bug <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"Nama file"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"Judul bug"</string>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 7d78c08..9ef05c5 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -16,6 +16,7 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
import android.service.quicksettings.Tile;
import com.android.systemui.plugins.annotations.DependsOn;
@@ -66,6 +67,10 @@
State getState();
+ default LogMaker populate(LogMaker logMaker) {
+ return logMaker;
+ }
+
@ProvidesInterface(version = Callback.VERSION)
public interface Callback {
public static final int VERSION = 1;
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1ea4f83..bd539ea 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2036,7 +2036,9 @@
<!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
<string name="running_foreground_services_title">Apps running in background</string>
- <!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
+ <!-- Descriptive text of an item in the "running foreground services" dialog, telling the
+ user what will happen when they tap on that item (which is an application that has
+ been identified for them as running). [CHAR LIMIT=NONE] -->
<string name="running_foreground_services_msg">Tap for details on battery and data usage</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
index 49e780c..9d286cf 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
@@ -66,7 +66,7 @@
private DialogInterface.OnClickListener mAppClickListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
- String pkg = mPackages[which];
+ String pkg = mAdapter.getItem(which).packageName;
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", pkg, null));
startActivity(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 7139d59..af02e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -37,6 +37,7 @@
private static final int PULSE_REASONS = 5;
+ public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
public static final int PULSE_REASON_NOTIFICATION = 1;
public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 34d621f..5526e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -107,6 +107,7 @@
private final ArrayList<State> mQueuedRequests = new ArrayList<>();
private State mState = State.UNINITIALIZED;
+ private int mPulseReason;
private boolean mWakeLockHeldForCurrentState = false;
public DozeMachine(Service service, AmbientDisplayConfiguration config,
@@ -134,6 +135,20 @@
*/
@MainThread
public void requestState(State requestedState) {
+ Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE);
+ requestState(requestedState, DozeLog.PULSE_REASON_NONE);
+ }
+
+ @MainThread
+ public void requestPulse(int pulseReason) {
+ // Must not be called during a transition. There's no inherent problem with that,
+ // but there's currently no need to execute from a transition and it simplifies the
+ // code to not have to worry about keeping the pulseReason in mQueuedRequests.
+ Preconditions.checkState(!isExecutingTransition());
+ requestState(State.DOZE_REQUEST_PULSE, pulseReason);
+ }
+
+ private void requestState(State requestedState, int pulseReason) {
Assert.isMainThread();
if (DEBUG) {
Log.i(TAG, "request: current=" + mState + " req=" + requestedState,
@@ -147,7 +162,7 @@
for (int i = 0; i < mQueuedRequests.size(); i++) {
// Transitions in Parts can call back into requestState, which will
// cause mQueuedRequests to grow.
- transitionTo(mQueuedRequests.get(i));
+ transitionTo(mQueuedRequests.get(i), pulseReason);
}
mQueuedRequests.clear();
mWakeLock.release();
@@ -166,6 +181,20 @@
return mState;
}
+ /**
+ * @return the current pulse reason.
+ *
+ * This is only valid if the machine is currently in one of the pulse states.
+ */
+ @MainThread
+ public int getPulseReason() {
+ Assert.isMainThread();
+ Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE
+ || mState == State.DOZE_PULSING
+ || mState == State.DOZE_PULSE_DONE, "must be in pulsing state, but is " + mState);
+ return mPulseReason;
+ }
+
/** Requests the PowerManager to wake up now. */
public void wakeUp() {
mDozeService.requestWakeUp();
@@ -175,7 +204,7 @@
return !mQueuedRequests.isEmpty();
}
- private void transitionTo(State requestedState) {
+ private void transitionTo(State requestedState, int pulseReason) {
State newState = transitionPolicy(requestedState);
if (DEBUG) {
@@ -191,6 +220,7 @@
State oldState = mState;
mState = newState;
+ updatePulseReason(newState, oldState, pulseReason);
performTransitionOnComponents(oldState, newState);
updateScreenState(newState);
updateWakeLockState(newState);
@@ -198,6 +228,14 @@
resolveIntermediateState(newState);
}
+ private void updatePulseReason(State newState, State oldState, int pulseReason) {
+ if (newState == State.DOZE_REQUEST_PULSE) {
+ mPulseReason = pulseReason;
+ } else if (oldState == State.DOZE_PULSE_DONE) {
+ mPulseReason = DozeLog.PULSE_REASON_NONE;
+ }
+ }
+
private void performTransitionOnComponents(State oldState, State newState) {
for (Part p : mParts) {
p.transitionTo(oldState, newState);
@@ -281,7 +319,8 @@
case INITIALIZED:
case DOZE_PULSE_DONE:
transitionTo(mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)
- ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE);
+ ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE,
+ DozeLog.PULSE_REASON_NONE);
break;
default:
break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index a721bf8..693a690 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -238,7 +238,7 @@
mDozeHost.isPulsingBlocked());
return;
}
- mMachine.requestState(DozeMachine.State.DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(reason);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 4e72bdf..22869d8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -83,7 +83,7 @@
unscheduleTimeTick();
break;
case DOZE_REQUEST_PULSE:
- pulseWhileDozing(DozeLog.PULSE_REASON_NOTIFICATION /* TODO */);
+ pulseWhileDozing(mMachine.getPulseReason());
break;
case DOZE_PULSE_DONE:
mHost.abortPulsing();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index df03fdc..bdc0871 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -107,12 +107,12 @@
}
@Override
- public void onPinnedActivityRestartAttempt() {
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) {
if (!checkCurrentUserId(false /* debug */)) {
return;
}
- mTouchHandler.getMotionHelper().expandPip();
+ mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index fc52a2e..5121c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -140,14 +140,25 @@
* Resizes the pinned stack back to fullscreen.
*/
void expandPip() {
+ expandPip(false /* skipAnimation */);
+ }
+
+ /**
+ * Resizes the pinned stack back to fullscreen.
+ */
+ void expandPip(boolean skipAnimation) {
cancelAnimations();
mHandler.post(() -> {
try {
- mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
- true /* allowResizeInDockedMode */, true /* preserveWindows */,
- true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+ if (skipAnimation) {
+ mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */);
+ } else {
+ mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
+ true /* allowResizeInDockedMode */, true /* preserveWindows */,
+ true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+ }
} catch (RemoteException e) {
- Log.e(TAG, "Error showing PiP menu activity", e);
+ Log.e(TAG, "Error expanding PiP activity", e);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 657f08b..9735bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -625,7 +625,7 @@
}
@Override
- public void onPinnedActivityRestartAttempt() {
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) {
if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
if (!checkCurrentUserId(DEBUG)) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f5e096eb..c4d88ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.metrics.LogMaker;
import android.os.Handler;
import android.os.Message;
import android.service.quicksettings.Tile;
@@ -30,6 +31,7 @@
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
@@ -179,7 +181,7 @@
public void openDetails(String subPanel) {
QSTile tile = getTile(subPanel);
- showDetailAdapter(true, tile.getDetailAdapter(), new int[] {getWidth() / 2, 0});
+ showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
}
private QSTile getTile(String subPanel) {
@@ -485,8 +487,9 @@
private void logTiles() {
for (int i = 0; i < mRecords.size(); i++) {
- TileRecord tileRecord = mRecords.get(i);
- mMetricsLogger.visible(tileRecord.tile.getMetricsCategory());
+ QSTile tile = mRecords.get(i).tile;
+ mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory())
+ .setType(MetricsEvent.TYPE_OPEN)));
}
}
@@ -544,12 +547,13 @@
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3;
+
@Override
public void handleMessage(Message msg) {
if (msg.what == SHOW_DETAIL) {
- handleShowDetail((Record)msg.obj, msg.arg1 != 0);
+ handleShowDetail((Record) msg.obj, msg.arg1 != 0);
} else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
- announceForAccessibility((CharSequence)msg.obj);
+ announceForAccessibility((CharSequence) msg.obj);
}
}
}
@@ -569,8 +573,11 @@
public interface QSTileLayout {
void addTile(TileRecord tile);
+
void removeTile(TileRecord tile);
+
int getOffsetTop(TileRecord tile);
+
boolean updateResources();
void setListening(boolean listening);
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 dc9176f..017365f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -157,7 +157,7 @@
}
@Override
- protected LogMaker populate(LogMaker logMaker) {
+ public LogMaker populate(LogMaker logMaker) {
return super.populate(logMaker).setComponentName(mComponent);
}
@@ -275,7 +275,6 @@
} catch (RemoteException e) {
// Called through wrapper, won't happen here.
}
- MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 976efb2..32af230 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -175,7 +175,7 @@
mHandler.sendEmptyMessage(H.LONG_CLICK);
}
- protected LogMaker populate(LogMaker logMaker) {
+ public LogMaker populate(LogMaker logMaker) {
if (mState instanceof BooleanState) {
logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a9e1f61..f431517 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -158,7 +158,7 @@
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
public void onActivityPinned(String packageName) { }
public void onActivityUnpinned() { }
- public void onPinnedActivityRestartAttempt() { }
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
public void onPinnedStackAnimationStarted() { }
public void onPinnedStackAnimationEnded() { }
public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
@@ -223,10 +223,11 @@
}
@Override
- public void onPinnedActivityRestartAttempt()
+ public void onPinnedActivityRestartAttempt(boolean clearedTask)
throws RemoteException{
mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
- mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+ mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
+ .sendToTarget();
}
@Override
@@ -1294,7 +1295,8 @@
}
case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+ mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
+ msg.arg1 != 0);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 5817e92..36be49d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -279,6 +279,7 @@
}
MetricsLogger.action(mSv.getContext(), MetricsEvent.OVERVIEW_SCROLL);
+ mLastY = mDownY = y;
}
}
if (mIsScrolling) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 072373b..8887bea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -86,6 +86,11 @@
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
private static final int MENU_VIEW_INDEX = 0;
+ public interface LayoutListener {
+ public void onLayout();
+ }
+
+ private LayoutListener mLayoutListener;
private final NotificationInflater mNotificationInflater;
private int mIconTransformContentShift;
private int mIconTransformContentShiftNoIcon;
@@ -1608,6 +1613,14 @@
mIsSystemChildExpanded = expanded;
}
+ public void setLayoutListener(LayoutListener listener) {
+ mLayoutListener = listener;
+ }
+
+ public void removeListener() {
+ mLayoutListener = null;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -1616,6 +1629,9 @@
mMenuRow.onHeightUpdate();
}
updateContentShiftHeight();
+ if (mLayoutListener != null) {
+ mLayoutListener.onLayout();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index 8d3bff2..cf910a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -44,7 +44,8 @@
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
-public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener {
+public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
+ ExpandableNotificationRow.LayoutListener {
private static final boolean DEBUG = false;
private static final String TAG = "swipe";
@@ -167,13 +168,14 @@
@Override
public void onConfigurationChanged() {
- mParent.post(new Runnable() {
- @Override
- public void run() {
- mIconsPlaced = false; // Force icons to be re-placed
- setMenuLocation();
- }
- });
+ mParent.setLayoutListener(this);
+ }
+
+ @Override
+ public void onLayout() {
+ mIconsPlaced = false; // Force icons to be re-placed
+ setMenuLocation();
+ mParent.removeListener();
}
private void createMenuViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index d9ac4d5..8588668 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -167,6 +167,10 @@
}
public void setColors(@NonNull ColorExtractor.GradientColors colors) {
+ setColors(colors, false);
+ }
+
+ public void setColors(@NonNull ColorExtractor.GradientColors colors, boolean animated) {
if (colors == null) {
throw new IllegalArgumentException("Colors cannot be null");
}
@@ -174,7 +178,7 @@
return;
}
mColors.set(colors);
- updateColorWithTint();
+ updateColorWithTint(animated);
}
@VisibleForTesting
@@ -183,14 +187,18 @@
}
public void setTint(int color) {
+ setTint(color, false);
+ }
+
+ public void setTint(int color, boolean animated) {
if (mTintColor == color) {
return;
}
mTintColor = color;
- updateColorWithTint();
+ updateColorWithTint(animated);
}
- private void updateColorWithTint() {
+ private void updateColorWithTint(boolean animated) {
if (mDrawable instanceof GradientDrawable) {
// Optimization to blend colors and avoid a color filter
GradientDrawable drawable = (GradientDrawable) mDrawable;
@@ -199,7 +207,7 @@
tintAmount);
int secondaryTinted = ColorUtils.blendARGB(mColors.getSecondaryColor(), mTintColor,
tintAmount);
- drawable.setColors(mainTinted, secondaryTinted, false);
+ drawable.setColors(mainTinted, secondaryTinted, animated);
} else {
if (mColorFilter == null) {
mColorFilter = new PorterDuffColorFilter(mTintColor, PorterDuff.Mode.SRC_OVER);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
index 82910b8..454edbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
@@ -34,7 +34,7 @@
* A utility class to colorize bitmaps with a color gradient and a special blending mode
*/
public class ImageGradientColorizer {
- public Bitmap colorize(Drawable drawable, int backgroundColor) {
+ public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
int size = Math.min(width, height);
@@ -70,8 +70,6 @@
0, 0, 0, 1, 0,
});
- drawable.setColorFilter(new ColorMatrixColorFilter(m));
- drawable.draw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
LinearGradient linearGradient = new LinearGradient(0, 0, size, 0,
new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
@@ -81,16 +79,27 @@
Canvas fadeInCanvas = new Canvas(fadeIn);
drawable.clearColorFilter();
drawable.draw(fadeInCanvas);
+
+ if (isRtl) {
+ // Let's flip the gradient
+ fadeInCanvas.translate(size, 0);
+ fadeInCanvas.scale(-1, 1);
+ }
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
fadeInCanvas.drawPaint(paint);
+
+ Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ coloredPaint.setColorFilter(new ColorMatrixColorFilter(m));
+ coloredPaint.setAlpha((int) (0.5f * 255));
+ canvas.drawBitmap(fadeIn, 0, 0, coloredPaint);
+
+ linearGradient = new LinearGradient(0, 0, size, 0,
+ new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
+ new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
+ paint.setShader(linearGradient);
+ fadeInCanvas.drawPaint(paint);
canvas.drawBitmap(fadeIn, 0, 0, null);
- linearGradient = new LinearGradient(0, 0, size, 0,
- new int[] {backgroundColor, Color.argb(0.5f, tr, tg, tb), 0},
- new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
- paint.setShader(linearGradient);
- paint.setXfermode(null);
- canvas.drawPaint(paint);
return newBitmap;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index cef225b..52c053f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Icon;
import android.support.v4.graphics.ColorUtils;
import android.support.v7.graphics.Palette;
+import android.util.LayoutDirection;
import com.android.systemui.R;
@@ -187,7 +188,9 @@
: R.color.notification_material_background_color;
backgroundColor = mContext.getColor(id);
}
- Bitmap colorized = mColorizer.colorize(drawable, backgroundColor);
+ Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
+ mContext.getResources().getConfiguration().getLayoutDirection() ==
+ LayoutDirection.RTL);
builder.setLargeIcon(Icon.createWithBitmap(colorized));
}
}
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 2be5b83..d29b356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -277,8 +277,8 @@
mScrimInFront.setColors(mLockColors);
mScrimBehind.setColors(mLockColors);
} else {
- mScrimInFront.setColors(mSystemColors);
- mScrimBehind.setColors(mSystemColors);
+ mScrimInFront.setColors(mSystemColors, true);
+ mScrimBehind.setColors(mSystemColors, true);
}
}
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 b89b062..28895d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4062,7 +4062,10 @@
public void showKeyguard() {
mKeyguardRequested = true;
- updateIsKeyguard();
+ // Unconditionally show keyguard again. There's some logic that relies on this
+ // being called even when the keyguard is already showing, e.g. for updating
+ // sensitiveness of notifications and making sure the panels are expanded.
+ showKeyguardImpl();
}
public boolean hideKeyguard() {
@@ -4075,7 +4078,10 @@
// there's no surface we can show to the user.
boolean keyguardForDozing = mDozingRequested && !mDeviceInteractive;
boolean shouldBeKeyguard = mKeyguardRequested || keyguardForDozing;
- if (shouldBeKeyguard && !mIsKeyguard) {
+ if (keyguardForDozing) {
+ updatePanelExpansionForKeyguard();
+ }
+ if (shouldBeKeyguard) {
showKeyguardImpl();
} else if (!shouldBeKeyguard && mIsKeyguard) {
return hideKeyguardImpl();
@@ -4103,11 +4109,7 @@
// Keyguard.
mNotificationPanel.setTouchDisabled(true);
}
- if (mState == StatusBarState.KEYGUARD) {
- instantExpandNotificationsPanel();
- } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
- instantCollapseNotificationPanel();
- }
+ updatePanelExpansionForKeyguard();
mLeaveOpenOnKeyguardHide = false;
if (mDraggedDownRow != null) {
mDraggedDownRow.setUserLocked(false);
@@ -4118,6 +4120,14 @@
mAssistManager.onLockscreenShown();
}
+ private void updatePanelExpansionForKeyguard() {
+ if (mState == StatusBarState.KEYGUARD) {
+ instantExpandNotificationsPanel();
+ } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
+ instantCollapseNotificationPanel();
+ }
+ }
+
private void onLaunchTransitionFadingEnded() {
mNotificationPanel.setAlpha(1.0f);
mNotificationPanel.onAffordanceLaunchEnded();
@@ -4506,7 +4516,6 @@
}
private void instantExpandNotificationsPanel() {
-
// Make our window larger and the panel expanded.
makeExpandedVisible(true);
mNotificationPanel.expand(false /* animate */);
@@ -5087,6 +5096,7 @@
mDozingRequested = true;
DozeLog.traceDozing(mContext, mDozing);
updateDozing();
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index a2d1baf..c726189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -47,6 +47,11 @@
public void onPhoneStateChanged(int phoneState) {
update();
}
+
+ @Override
+ public void onRefreshCarrierInfo() {
+ update();
+ }
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 24d8b92..fa6ba96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -106,7 +106,7 @@
public void testPulseDone_goesToDoze() {
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
mMachine.requestState(INITIALIZED);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
mMachine.requestState(DOZE_PULSE_DONE);
@@ -119,7 +119,7 @@
public void testPulseDone_goesToAoD() {
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
mMachine.requestState(INITIALIZED);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
mMachine.requestState(DOZE_PULSE_DONE);
@@ -163,7 +163,7 @@
public void testWakeLock_heldInPulseStates() {
mMachine.requestState(INITIALIZED);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
assertTrue(mWakeLockFake.isHeld());
mMachine.requestState(DOZE_PULSING);
@@ -186,7 +186,7 @@
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
mMachine.requestState(DOZE_PULSE_DONE);
@@ -198,9 +198,9 @@
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSE_DONE);
}
@@ -209,7 +209,7 @@
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSE_DONE);
}
@@ -235,7 +235,7 @@
public void testScreen_onInPulse() {
mMachine.requestState(INITIALIZED);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
assertEquals(Display.STATE_ON, mServiceFake.screenState);
@@ -246,7 +246,7 @@
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
assertEquals(Display.STATE_OFF, mServiceFake.screenState);
}
@@ -256,7 +256,7 @@
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE_AOD);
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
}
@@ -270,12 +270,43 @@
return null;
}).when(mPartMock).transitionTo(any(), eq(DOZE_REQUEST_PULSE));
- mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
assertEquals(DOZE_PULSING, mMachine.getState());
}
@Test
+ public void testPulseReason_getMatchesRequest() {
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP);
+
+ assertEquals(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
+ }
+
+ @Test
+ public void testPulseReason_getFromTransition() {
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE);
+ doAnswer(inv -> {
+ DozeMachine.State newState = inv.getArgument(1);
+ if (newState == DOZE_REQUEST_PULSE
+ || newState == DOZE_PULSING
+ || newState == DOZE_PULSE_DONE) {
+ assertEquals(DozeLog.PULSE_REASON_NOTIFICATION, mMachine.getPulseReason());
+ } else {
+ assertTrue("unexpected state " + newState,
+ newState == DOZE || newState == DOZE_AOD);
+ }
+ return null;
+ }).when(mPartMock).transitionTo(any(), any());
+
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestState(DOZE_PULSING);
+ mMachine.requestState(DOZE_PULSE_DONE);
+ }
+
+ @Test
public void testWakeUp_wakesUp() {
mMachine.wakeUp();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 12e75a1..8934460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -22,26 +22,28 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.withSettings;
import android.app.Instrumentation;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
+import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
-import org.mockito.Answers;
-import org.mockito.MockSettings;
+import org.junit.runner.RunWith;
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class DozeTriggersTest {
private Context mContext;
private DozeTriggers mTriggers;
@@ -49,7 +51,7 @@
private DozeHostFake mHost;
private AmbientDisplayConfiguration mConfig;
private DozeParameters mParameters;
- private SensorManagerFake mSensors;
+ private FakeSensorManager mSensors;
private Handler mHandler;
private WakeLock mWakeLock;
private Instrumentation mInstrumentation;
@@ -68,7 +70,7 @@
mHost = new DozeHostFake();
mConfig = DozeConfigurationUtil.createMockConfig();
mParameters = DozeConfigurationUtil.createMockParameters();
- mSensors = new SensorManagerFake(mContext);
+ mSensors = new FakeSensorManager(mContext);
mHandler = new Handler(Looper.getMainLooper());
mWakeLock = new WakeLockFake();
@@ -79,32 +81,32 @@
}
@Test
- @Ignore("setup crashes on virtual devices")
public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception {
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
mInstrumentation.runOnMainSync(()->{
mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
- mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE);
+ mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
mHost.callback.onNotificationHeadsUp();
});
mInstrumentation.runOnMainSync(() -> {
- mSensors.PROXIMITY.sendProximityResult(false); /* Near */
+ mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
});
verify(mMachine, never()).requestState(any());
+ verify(mMachine, never()).requestPulse(anyInt());
mInstrumentation.runOnMainSync(()->{
mHost.callback.onNotificationHeadsUp();
});
mInstrumentation.runOnMainSync(() -> {
- mSensors.PROXIMITY.sendProximityResult(true); /* Far */
+ mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
});
- verify(mMachine).requestState(DozeMachine.State.DOZE_REQUEST_PULSE);
+ verify(mMachine).requestPulse(anyInt());
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
similarity index 60%
rename from packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java
rename to packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
index 5b4b891..30be665 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
@@ -11,10 +11,10 @@
* 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.doze;
+package com.android.systemui.utils.hardware;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -33,6 +33,8 @@
import com.google.android.collect.Lists;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -44,18 +46,38 @@
* Note that this class ignores the "Handler" argument, so the test is responsible for calling the
* listener on the right thread.
*/
-public class SensorManagerFake extends SensorManager {
+public class FakeSensorManager extends SensorManager {
- public MockSensor PROXIMITY;
+ private final MockProximitySensor mMockProximitySensor;
- public SensorManagerFake(Context context) {
- PROXIMITY = new MockSensor(context.getSystemService(SensorManager.class)
- .getDefaultSensor(Sensor.TYPE_PROXIMITY));
+ public FakeSensorManager(Context context) throws Exception {
+ Sensor proxSensor = context.getSystemService(SensorManager.class)
+ .getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (proxSensor == null) {
+ // No prox? Let's create a fake one!
+ proxSensor = createSensor(Sensor.TYPE_PROXIMITY);
+ }
+ mMockProximitySensor = new MockProximitySensor(proxSensor);
+ }
+
+ public MockProximitySensor getMockProximitySensor() {
+ return mMockProximitySensor;
+ }
+
+ @Override
+ public Sensor getDefaultSensor(int type) {
+ Sensor s = super.getDefaultSensor(type);
+ if (s != null) {
+ return s;
+ }
+ // Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just
+ // return non-wakeup sensors if we can't find a wakeup sensor.
+ return getDefaultSensor(type, false /* wakeup */);
}
@Override
protected List<Sensor> getFullSensorList() {
- return Lists.newArrayList(PROXIMITY.sensor);
+ return Lists.newArrayList(mMockProximitySensor.sensor);
}
@Override
@@ -65,8 +87,8 @@
@Override
protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
- if (sensor == PROXIMITY.sensor || sensor == null) {
- PROXIMITY.listeners.remove(listener);
+ if (sensor == mMockProximitySensor.sensor || sensor == null) {
+ mMockProximitySensor.listeners.remove(listener);
}
}
@@ -74,8 +96,8 @@
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
int delayUs,
Handler handler, int maxReportLatencyUs, int reservedFlags) {
- if (sensor == PROXIMITY.sensor) {
- PROXIMITY.listeners.add(listener);
+ if (sensor == mMockProximitySensor.sensor) {
+ mMockProximitySensor.listeners.add(listener);
return true;
}
return false;
@@ -141,11 +163,44 @@
return false;
}
- public class MockSensor {
+ private Sensor createSensor(int type) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+
+ setSensorType(sensor, type);
+ setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
+ setSensorField(sensor, "mVendor", "Mock Vendor");
+ setSensorField(sensor, "mVersion", 1);
+ setSensorField(sensor, "mHandle", -1);
+ setSensorField(sensor, "mMaxRange", 10);
+ setSensorField(sensor, "mResolution", 1);
+ setSensorField(sensor, "mPower", 1);
+ setSensorField(sensor, "mMinDelay", 1000);
+ setSensorField(sensor, "mMaxDelay", 1000000000);
+ setSensorField(sensor, "mFlags", 0);
+ setSensorField(sensor, "mId", -1);
+
+ return sensor;
+ }
+
+ private void setSensorField(Sensor sensor, String fieldName, Object value) throws Exception {
+ Field field = Sensor.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(sensor, value);
+ }
+
+ private void setSensorType(Sensor sensor, int type) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ }
+
+ public class MockProximitySensor {
final Sensor sensor;
final ArraySet<SensorEventListener> listeners = new ArraySet<>();
- private MockSensor(Sensor sensor) {
+ private MockProximitySensor(Sensor sensor) {
this.sensor = sensor;
}
diff --git a/preloaded-classes b/preloaded-classes
index 892c593..493e980 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1820,6 +1820,7 @@
android.text.GetChars
android.text.GraphicsOperations
android.text.Html
+android.text.Html$HtmlParser
android.text.Hyphenator
android.text.InputFilter
android.text.InputType
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f9b4372..9fa6229 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3962,6 +3962,13 @@
// OPEN: Settings > System > Languages & input > Advanced > Lift to open camera
SETTINGS_GESTURE_CAMERA_LIFT_TRIGGER = 986;
+ // OPEN: Settings > Battery > High Usage > Abnormal app page
+ // CATEGORY: SETTINGS
+ FUELGAUGE_ANOMALY_DETAIL = 987;
+
+ // OPEN: Settings > Battery > High Usage
+ DIALOG_HANDLE_ANOMALY = 988;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/rs/java/android/renderscript/ScriptIntrinsicLUT.java b/rs/java/android/renderscript/ScriptIntrinsicLUT.java
index 69ff64a..e90462d 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicLUT.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicLUT.java
@@ -56,6 +56,10 @@
}
+ public void destroy() {
+ mTables.destroy();
+ super.destroy();
+ }
private void validate(int index, int value) {
if (index < 0 || index > 255) {
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 68ade63..cbf97dd 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -20,7 +20,9 @@
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.os.Bundle;
+import android.util.DebugUtils;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import java.util.Arrays;
import java.util.Objects;
@@ -40,6 +42,10 @@
*/
public static boolean sVerbose = false;
+ private Helper() {
+ throw new UnsupportedOperationException("contains static members only");
+ }
+
static void append(StringBuilder builder, Bundle bundle) {
if (bundle == null || !sVerbose) {
builder.append("null");
@@ -62,8 +68,8 @@
return builder.toString();
}
- private Helper() {
- throw new UnsupportedOperationException("contains static members only");
+ static String getUpdateActionAsString(int action) {
+ return DebugUtils.flagsToString(AutofillManager.class, "ACTION_", action);
}
static ViewNode findViewNodeById(@NonNull AssistStructure structure, @NonNull AutofillId id) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 018fb68..23b734a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -25,9 +25,13 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static com.android.server.autofill.Helper.findViewNodeById;
+import static com.android.server.autofill.Helper.getUpdateActionAsString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
-import static com.android.server.autofill.Helper.findViewNodeById;
+import static com.android.server.autofill.ViewState.STATE_AUTOFILLED;
+import static com.android.server.autofill.ViewState.STATE_FILLABLE;
+import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -221,7 +225,7 @@
final int numContexts = mContexts.size();
for (int i = 0; i < numContexts; i++) {
- fillStructureWithAllowedValues(mContexts.get(i).getStructure());
+ fillStructureWithAllowedValues(mContexts.get(i).getStructure(), flags);
}
request = new FillRequest(requestId, mContexts, mClientState, flags);
@@ -235,10 +239,12 @@
* Updates values of the nodes in the structure so that:
* - proper node is focused
* - autofillValue is sent back to service when it was previously autofilled
+ * - autofillValue is sent in the view used to force a request
*
* @param structure The structure to be filled
+ * @param flags The flags that started the session
*/
- private void fillStructureWithAllowedValues(@NonNull AssistStructure structure) {
+ private void fillStructureWithAllowedValues(@NonNull AssistStructure structure, int flags) {
final int numViewStates = mViewStates.size();
for (int i = 0; i < numViewStates; i++) {
final ViewState viewState = mViewStates.valueAt(i);
@@ -249,16 +255,23 @@
continue;
}
- final AutofillValue initialValue = viewState.getInitialValue();
+ final AutofillValue currentValue = viewState.getCurrentValue();
final AutofillValue filledValue = viewState.getAutofilledValue();
final AutofillOverlay overlay = new AutofillOverlay();
- if (filledValue != null && !filledValue.equals(initialValue)) {
- overlay.value = filledValue;
- }
- if (mCurrentViewId != null) {
- overlay.focused = mCurrentViewId.equals(viewState.id);
+
+ // Sanitizes the value if the current value matches what the service sent.
+ if (filledValue != null && filledValue.equals(currentValue)) {
+ overlay.value = currentValue;
}
+ if (mCurrentViewId != null) {
+ // Updates the focus value.
+ overlay.focused = mCurrentViewId.equals(viewState.id);
+ // Sanitizes the value of the focused field in a manual request.
+ if (overlay.focused && (flags & FLAG_MANUAL_REQUEST) != 0) {
+ overlay.value = currentValue;
+ }
+ }
node.setAutofillOverlay(overlay);
}
}
@@ -883,6 +896,40 @@
}
/**
+ * Starts (if necessary) a new fill request upon entering a view.
+ *
+ * <p>A new request will be started in 2 scenarios:
+ * <ol>
+ * <li>If the user manually requested autofill after the view was already filled.
+ * <li>If the view is part of a new partition.
+ * </ol>
+ *
+ * @param id The id of the view that is entered.
+ * @param viewState The view that is entered.
+ * @param flags The flag that was passed by the AutofillManager.
+ */
+ private void requestNewFillResponseIfNecessaryLocked(@NonNull AutofillId id,
+ @NonNull ViewState viewState, int flags) {
+ // First check if this is a manual request after view was autofilled.
+ final int state = viewState.getState();
+ final boolean restart = (state & STATE_AUTOFILLED) != 0
+ && (flags & FLAG_MANUAL_REQUEST) != 0;
+ if (restart) {
+ if (sDebug) Slog.d(TAG, "Re-starting session on view " + id);
+ viewState.setState(STATE_RESTARTED_SESSION);
+ requestNewFillResponseLocked(flags);
+ return;
+ }
+
+ // If it's not, then check if it it should start a partition.
+ if (shouldStartNewPartitionLocked(id)) {
+ if (sDebug) Slog.d(TAG, "Starting partition for view id " + id);
+ viewState.setState(ViewState.STATE_STARTED_PARTITION);
+ requestNewFillResponseLocked(flags);
+ }
+ }
+
+ /**
* Determines if a new partition should be started for an id.
*
* @param id The id of the view that is entered
@@ -938,6 +985,10 @@
+ id + " destroyed");
return;
}
+ if (sVerbose) {
+ Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + getUpdateActionAsString(action)
+ + ", flags=" + flags);
+ }
ViewState viewState = mViewStates.get(id);
if (viewState == null) {
@@ -992,13 +1043,7 @@
}
break;
case ACTION_VIEW_ENTERED:
- if (shouldStartNewPartitionLocked(id)) {
- if (sDebug) {
- Slog.d(TAG, "Starting partition for view id " + viewState.id);
- }
- viewState.setState(ViewState.STATE_STARTED_PARTITION);
- requestNewFillResponseLocked(flags);
- }
+ requestNewFillResponseIfNecessaryLocked(id, viewState, flags);
// Remove the UI if the ViewState has changed.
if (mCurrentViewId != viewState.id) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index d114e14..561c603 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -47,23 +47,25 @@
private static final String TAG = "ViewState";
// NOTE: state constants must be public because of flagstoString().
- public static final int STATE_UNKNOWN = 0x00;
+ public static final int STATE_UNKNOWN = 0x000;
/** Initial state. */
- public static final int STATE_INITIAL = 0x01;
+ public static final int STATE_INITIAL = 0x001;
/** View id is present in a dataset returned by the service. */
- public static final int STATE_FILLABLE = 0x02;
+ public static final int STATE_FILLABLE = 0x002;
/** View was autofilled after user selected a dataset. */
- public static final int STATE_AUTOFILLED = 0x04;
+ public static final int STATE_AUTOFILLED = 0x004;
/** View value was changed, but not by the service. */
- public static final int STATE_CHANGED = 0x08;
+ public static final int STATE_CHANGED = 0x008;
/** Set only in the View that started a session. */
- public static final int STATE_STARTED_SESSION = 0x10;
+ public static final int STATE_STARTED_SESSION = 0x010;
/** View that started a new partition when focused on. */
- public static final int STATE_STARTED_PARTITION = 0x20;
+ public static final int STATE_STARTED_PARTITION = 0x020;
/** User select a dataset in this view, but service must authenticate first. */
- public static final int STATE_WAITING_DATASET_AUTH = 0x40;
+ public static final int STATE_WAITING_DATASET_AUTH = 0x040;
/** Service does not care about this view. */
- public static final int STATE_IGNORED = 0x80;
+ public static final int STATE_IGNORED = 0x080;
+ /** User manually request autofill in this view, after it was already autofilled. */
+ public static final int STATE_RESTARTED_SESSION = 0x100;
public final AutofillId id;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 45ecbdb..1e74c55 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -776,7 +776,7 @@
}
// High level policy: apps are generally ineligible for backup if certain conditions apply
- public static boolean appIsEligibleForBackup(ApplicationInfo app) {
+ public static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
// 1. their manifest states android:allowBackup="false"
if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
return false;
@@ -792,15 +792,18 @@
return false;
}
- return true;
+ // Everything else checks out; the only remaining roadblock would be if the
+ // package were disabled
+ return !appIsDisabled(app, pm);
}
- // Checks if the app is in a stopped state, that means it won't receive broadcasts.
+ // Checks if the app is in a stopped state. This is not part of the general "eligible for
+ // backup?" check because we *do* still need to restore data to apps in this state (e.g.
+ // newly-installing ones)
private static boolean appIsStopped(ApplicationInfo app) {
return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0);
}
- // We also avoid backups of 'disabled' apps
private static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
switch (pm.getApplicationEnabledSetting(app.packageName)) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
@@ -1528,7 +1531,8 @@
foundApps.add(pkgName); // all apps that we've addressed already
try {
PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
- if (appGetsFullBackup(pkg) && appIsEligibleForBackup(pkg.applicationInfo)) {
+ if (appGetsFullBackup(pkg)
+ && appIsEligibleForBackup(pkg.applicationInfo, mPackageManager)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
if (DEBUG) {
@@ -1547,7 +1551,8 @@
// New apps can arrive "out of band" via OTA and similar, so we also need to
// scan to make sure that we're tracking all full-backup candidates properly
for (PackageInfo app : apps) {
- if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
+ if (appGetsFullBackup(app)
+ && appIsEligibleForBackup(app.applicationInfo, mPackageManager)) {
if (!foundApps.contains(app.packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "New full backup app " + app.packageName + " found");
@@ -1576,7 +1581,8 @@
changed = true;
schedule = new ArrayList<FullBackupEntry>(apps.size());
for (PackageInfo info : apps) {
- if (appGetsFullBackup(info) && appIsEligibleForBackup(info.applicationInfo)) {
+ if (appGetsFullBackup(info)
+ && appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
schedule.add(new FullBackupEntry(info.packageName, 0));
}
}
@@ -2036,7 +2042,8 @@
for (String packageName : pkgList) {
try {
PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
- if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
+ if (appGetsFullBackup(app)
+ && appIsEligibleForBackup(app.applicationInfo, mPackageManager)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
@@ -2393,7 +2400,7 @@
long token = mAncestralToken;
synchronized (mQueueLock) {
- if (mEverStoredApps.contains(packageName)) {
+ if (mCurrentToken != 0 && mEverStoredApps.contains(packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "App in ever-stored, so using current token");
}
@@ -2440,7 +2447,7 @@
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
- if (!appIsEligibleForBackup(packageInfo.applicationInfo)) {
+ if (!appIsEligibleForBackup(packageInfo.applicationInfo, mPackageManager)) {
sendBackupOnPackageResult(observer, packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
@@ -2949,7 +2956,7 @@
try {
mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
PackageManager.GET_SIGNATURES);
- if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo)) {
+ if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo, mPackageManager)) {
// The manifest has changed but we had a stale backup request pending.
// This won't happen again because the app won't be requesting further
// backups.
@@ -4397,7 +4404,7 @@
Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
while (iter.hasNext()) {
PackageInfo pkg = iter.next().getValue();
- if (!appIsEligibleForBackup(pkg.applicationInfo)
+ if (!appIsEligibleForBackup(pkg.applicationInfo, mPackageManager)
|| appIsStopped(pkg.applicationInfo)) {
iter.remove();
if (DEBUG) {
@@ -4679,7 +4686,7 @@
PackageInfo info = mPackageManager.getPackageInfo(pkg,
PackageManager.GET_SIGNATURES);
mCurrentPackage = info;
- if (!appIsEligibleForBackup(info.applicationInfo)) {
+ if (!appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
// Cull any packages that have indicated that backups are not permitted,
// that run as system-domain uids but do not define their own backup agents,
// as well as any explicit mention of the 'special' shared-storage agent
@@ -8634,7 +8641,7 @@
continue;
}
- if (appIsEligibleForBackup(info.applicationInfo)) {
+ if (appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
@@ -10456,8 +10463,7 @@
final long oldId = Binder.clearCallingIdentity();
try {
String prevTransport = mTransportManager.selectTransport(transport);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT, transport);
+ updateStateForTransport(transport);
Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName()
+ " returning " + prevTransport);
return prevTransport;
@@ -10481,9 +10487,7 @@
@Override
public void onSuccess(String transportName) {
mTransportManager.selectTransport(transportName);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT,
- mTransportManager.getCurrentTransportName());
+ updateStateForTransport(mTransportManager.getCurrentTransportName());
Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString());
try {
listener.onSuccess(transportName);
@@ -10506,6 +10510,28 @@
Binder.restoreCallingIdentity(oldId);
}
+ private void updateStateForTransport(String newTransportName) {
+ // Publish the name change
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+
+ // And update our current-dataset bookkeeping
+ IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName);
+ if (transport != null) {
+ try {
+ mCurrentToken = transport.getCurrentRestoreSet();
+ } catch (Exception e) {
+ // Oops. We can't know the current dataset token, so reset and figure it out
+ // when we do the next k/v backup operation on this transport.
+ mCurrentToken = 0;
+ }
+ } else {
+ // The named transport isn't bound at this particular moment, so we can't
+ // know yet what its current dataset token is. Reset as above.
+ mCurrentToken = 0;
+ }
+ }
+
// Supply the configuration Intent for the given transport. If the name is not one
// of the available transports, or if the transport does not supply any configuration
// UI, the method returns null.
@@ -10814,9 +10840,8 @@
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
- if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
- appIsStopped(packageInfo.applicationInfo) ||
- appIsDisabled(packageInfo.applicationInfo, mPackageManager)) {
+ if (!appIsEligibleForBackup(packageInfo.applicationInfo, mPackageManager) ||
+ appIsStopped(packageInfo.applicationInfo)) {
return false;
}
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 92f00b8..8d91e0d 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -140,7 +140,7 @@
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
- if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo)) {
+ if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
pkgs.remove(a);
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 1e56e95..9e7a29e 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -357,7 +357,13 @@
if (svc != null) {
svc.selectBackupTransportAsync(transport, listener);
} else {
- listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ if (listener != null) {
+ try {
+ listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ } catch (RemoteException ex) {
+ // ignore
+ }
+ }
}
}
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 90caae8..fb2982e 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -182,13 +182,13 @@
String[] getBoundTransportNames() {
synchronized (mTransportLock) {
- return mBoundTransports.keySet().toArray(new String[0]);
+ return mBoundTransports.keySet().toArray(new String[mBoundTransports.size()]);
}
}
ComponentName[] getAllTransportCompenents() {
synchronized (mTransportLock) {
- return mValidTransports.keySet().toArray(new ComponentName[0]);
+ return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]);
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreDeleteObserver.java b/services/backup/java/com/android/server/backup/restore/RestoreDeleteObserver.java
index 5bc3eca..12be84c 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreDeleteObserver.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreDeleteObserver.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.restore;
import android.content.pm.IPackageDeleteObserver;
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
index 4cb0abc..6c9a222 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.restore;
import android.app.IBackupAgent;
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreInstallObserver.java b/services/backup/java/com/android/server/backup/restore/RestoreInstallObserver.java
index eff857a..3d506f1 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreInstallObserver.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreInstallObserver.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.restore;
import android.app.PackageInstallObserver;
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index a32635e..03862c4 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import android.content.pm.ApplicationInfo;
@@ -75,6 +91,7 @@
/**
* Old style: directly match the stored vs on device signature blocks.
*/
+ // TODO(b/37977154): Resolve questionable policies.
public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
if (target == null) {
return false;
@@ -94,7 +111,7 @@
}
// Allow unsigned apps, but not signed on one device and unsigned on the other
- // !!! TODO: is this the right policy?
+ // TODO(b/37977154): is this the right policy?
Signature[] deviceSigs = target.signatures;
if (RefactoredBackupManagerService.MORE_DEBUG) {
Slog.v(RefactoredBackupManagerService.TAG, "signaturesMatch(): stored=" + storedSigs
@@ -104,11 +121,12 @@
&& (deviceSigs == null || deviceSigs.length == 0)) {
return true;
}
+ // TODO(b/37977154): This allows empty stored signature, is this right?
if (storedSigs == null || deviceSigs == null) {
return false;
}
- // !!! TODO: this demands that every stored signature match one
+ // TODO(b/37977154): this demands that every stored signature match one
// that is present on device, and does not demand the converse.
// Is this this right policy?
int nStored = storedSigs.length;
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
index 53addfc..3b9b692 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME;
@@ -54,6 +70,15 @@
return null;
}
+ /**
+ * Adds given key-value pair in the bundle and returns the bundle. If bundle was null it will
+ * be created.
+ *
+ * @param extras - bundle where to add key-value to, if null a new bundle will be created.
+ * @param key - key.
+ * @param value - value.
+ * @return extras if it was not null and new bundle otherwise.
+ */
public static Bundle putMonitoringExtra(Bundle extras, String key, String value) {
if (extras == null) {
extras = new Bundle();
@@ -62,14 +87,15 @@
return extras;
}
- private static Bundle putMonitoringExtra(Bundle extras, String key, int value) {
- if (extras == null) {
- extras = new Bundle();
- }
- extras.putInt(key, value);
- return extras;
- }
-
+ /**
+ * Adds given key-value pair in the bundle and returns the bundle. If bundle was null it will
+ * be created.
+ *
+ * @param extras - bundle where to add key-value to, if null a new bundle will be created.
+ * @param key - key.
+ * @param value - value.
+ * @return extras if it was not null and new bundle otherwise.
+ */
public static Bundle putMonitoringExtra(Bundle extras, String key, long value) {
if (extras == null) {
extras = new Bundle();
@@ -78,6 +104,15 @@
return extras;
}
+ /**
+ * Adds given key-value pair in the bundle and returns the bundle. If bundle was null it will
+ * be created.
+ *
+ * @param extras - bundle where to add key-value to, if null a new bundle will be created.
+ * @param key - key.
+ * @param value - value.
+ * @return extras if it was not null and new bundle otherwise.
+ */
public static Bundle putMonitoringExtra(Bundle extras, String key, boolean value) {
if (extras == null) {
extras = new Bundle();
diff --git a/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java b/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java
index b5e5e08..eb939a8 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import android.app.backup.BackupProgress;
diff --git a/services/backup/java/com/android/server/backup/utils/BytesReadListener.java b/services/backup/java/com/android/server/backup/utils/BytesReadListener.java
index 1595153..34d85ff 100644
--- a/services/backup/java/com/android/server/backup/utils/BytesReadListener.java
+++ b/services/backup/java/com/android/server/backup/utils/BytesReadListener.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
/**
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java
index 0c868af..1b511860 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupRestoreObserverUtils.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import android.app.backup.IFullBackupRestoreObserver;
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index bfb4b25..4072a5e 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import android.content.pm.PackageInfo;
diff --git a/services/backup/java/com/android/server/backup/utils/PasswordUtils.java b/services/backup/java/com/android/server/backup/utils/PasswordUtils.java
index c90ec594..5ae79e2 100644
--- a/services/backup/java/com/android/server/backup/utils/PasswordUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/PasswordUtils.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import android.util.Slog;
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index cdda68e..c277c03 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import android.content.pm.ApplicationInfo;
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 25832c9..12c1af2 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.backup.utils;
import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 6597a5e..a86612e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1086,6 +1086,7 @@
if (timeZoneWasChanged) {
Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
intent.putExtra("time-zone", zone.getID());
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 114d761..fdc0bba 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -631,6 +631,11 @@
PackageManager.PERMISSION_GRANTED;
}
+ private boolean callerCanScoreNetworks() {
+ return mContext.checkCallingOrSelfPermission(permission.SCORE_NETWORKS) ==
+ PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public boolean clearScores() {
// Only the active scorer or the system should be allowed to flush all scores.
@@ -651,9 +656,9 @@
@Override
public boolean setActiveScorer(String packageName) {
// Only the system can set the active scorer
- if (!isCallerSystemProcess(getCallingUid()) && !callerCanRequestScores()) {
+ if (!isCallerSystemProcess(getCallingUid()) && !callerCanScoreNetworks()) {
throw new SecurityException(
- "Caller is neither the system process nor a score requester.");
+ "Caller is neither the system process or a network scorer.");
}
return mNetworkScorerAppManager.setActiveScorer(packageName);
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
index 8404025..42777bf 100644
--- a/services/core/java/com/android/server/NetworkScorerAppManager.java
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -208,11 +208,11 @@
*
* <p>The caller must have permission to write to {@link Settings.Global}.
*
- * @param packageName the packageName of the new scorer to use. If null, the scoring app will
- * revert back to the configured default. Otherwise, the scorer will only
- * be set if it is a valid scorer application.
- * @return true if the scorer was changed, or false if the package is not a valid scorer or
- * a valid network recommendation provider exists.
+ * @param packageName the packageName of the new scorer to use. If null, scoring will be forced
+ * off, otherwise the scorer will only be set if it is a valid scorer
+ * application.
+ * @return true if the package was a valid scorer (including <code>null</code>) and now
+ * represents the active scorer, false otherwise.
*/
@VisibleForTesting
public boolean setActiveScorer(String packageName) {
@@ -223,16 +223,19 @@
return true;
}
- if (packageName == null) {
- // revert to the default setting.
- packageName = getDefaultPackageSetting();
+ if (TextUtils.isEmpty(packageName)) {
+ Log.i(TAG, "Network scorer forced off, was: " + oldPackageName);
+ setNetworkRecommendationsPackage(null);
+ setNetworkRecommendationsEnabledSetting(
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF);
+ return true;
}
- Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
-
// We only make the change if the new package is valid.
if (getScorer(packageName) != null) {
+ Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
setNetworkRecommendationsPackage(packageName);
+ setNetworkRecommendationsEnabledSetting(NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON);
return true;
} else {
Log.w(TAG, "Requested network scorer is not valid: " + packageName);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 4ce76f4..e609bd7 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -157,10 +157,6 @@
private int[] mDataConnectionState;
- private boolean[] mDataConnectionPossible;
-
- private String[] mDataConnectionReason;
-
private ArrayList<String>[] mConnectedApns;
private LinkProperties[] mDataConnectionLinkProperties;
@@ -310,8 +306,6 @@
mDataActivationState = new int[numPhones];
mSignalStrength = new SignalStrength[numPhones];
mMessageWaiting = new boolean[numPhones];
- mDataConnectionPossible = new boolean[numPhones];
- mDataConnectionReason = new String[numPhones];
mCallForwarding = new boolean[numPhones];
mCellLocation = new Bundle[numPhones];
mDataConnectionLinkProperties = new LinkProperties[numPhones];
@@ -328,8 +322,6 @@
mSignalStrength[i] = new SignalStrength();
mMessageWaiting[i] = false;
mCallForwarding[i] = false;
- mDataConnectionPossible[i] = false;
- mDataConnectionReason[i] = "";
mCellLocation[i] = new Bundle();
mCellInfo.add(i, null);
mConnectedApns[i] = new ArrayList<String>();
@@ -1080,16 +1072,16 @@
}
}
- public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
+ public void notifyDataConnection(int state, boolean isDataAllowed,
String reason, String apn, String apnType, LinkProperties linkProperties,
NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
notifyDataConnectionForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state,
- isDataConnectivityPossible,reason, apn, apnType, linkProperties,
+ isDataAllowed,reason, apn, apnType, linkProperties,
networkCapabilities, networkType, roaming);
}
public void notifyDataConnectionForSubscriber(int subId, int state,
- boolean isDataConnectivityPossible, String reason, String apn, String apnType,
+ boolean isDataAllowed, String reason, String apn, String apnType,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int networkType, boolean roaming) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
@@ -1097,7 +1089,7 @@
}
if (VDBG) {
log("notifyDataConnectionForSubscriber: subId=" + subId
- + " state=" + state + " isDataConnectivityPossible=" + isDataConnectivityPossible
+ + " state=" + state + " isDataAllowed=" + isDataAllowed
+ " reason='" + reason
+ "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
+ " mRecords.size()=" + mRecords.size());
@@ -1125,8 +1117,6 @@
}
}
}
- mDataConnectionPossible[phoneId] = isDataConnectivityPossible;
- mDataConnectionReason[phoneId] = reason;
mDataConnectionLinkProperties[phoneId] = linkProperties;
mDataConnectionNetworkCapabilities[phoneId] = networkCapabilities;
if (mDataConnectionNetworkType[phoneId] != networkType) {
@@ -1173,7 +1163,7 @@
}
handleRemoveListLocked();
}
- broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
+ broadcastDataConnectionStateChanged(state, isDataAllowed, reason, apn,
apnType, linkProperties, networkCapabilities, roaming, subId);
broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
linkProperties, "");
@@ -1414,8 +1404,6 @@
pw.println("mCallForwarding=" + mCallForwarding[i]);
pw.println("mDataActivity=" + mDataActivity[i]);
pw.println("mDataConnectionState=" + mDataConnectionState[i]);
- pw.println("mDataConnectionPossible=" + mDataConnectionPossible[i]);
- pw.println("mDataConnectionReason=" + mDataConnectionReason[i]);
pw.println("mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]);
pw.println("mDataConnectionNetworkCapabilities=" +
mDataConnectionNetworkCapabilities[i]);
@@ -1544,7 +1532,7 @@
}
private void broadcastDataConnectionStateChanged(int state,
- boolean isDataConnectivityPossible,
+ boolean isDataAllowed,
String reason, String apn, String apnType, LinkProperties linkProperties,
NetworkCapabilities networkCapabilities, boolean roaming, int subId) {
// Note: not reporting to the battery stats service here, because the
@@ -1553,7 +1541,7 @@
Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
intent.putExtra(PhoneConstants.STATE_KEY,
PhoneConstantConversions.convertDataState(state).toString());
- if (!isDataConnectivityPossible) {
+ if (!isDataAllowed) {
intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true);
}
if (reason != null) {
diff --git a/services/core/java/com/android/server/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/Vr2dDisplay.java
similarity index 93%
rename from services/core/java/com/android/server/vr/CompatibilityDisplay.java
rename to services/core/java/com/android/server/Vr2dDisplay.java
index d7cdf08..1116e4e 100644
--- a/services/core/java/com/android/server/vr/CompatibilityDisplay.java
+++ b/services/core/java/com/android/server/Vr2dDisplay.java
@@ -3,7 +3,7 @@
import static android.view.Display.INVALID_DISPLAY;
import android.app.ActivityManagerInternal;
-import android.app.CompatibilityDisplayProperties;
+import android.app.Vr2dDisplayProperties;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -31,8 +31,8 @@
* Creates a 2D Virtual Display while VR Mode is enabled. This display will be used to run and
* render 2D app within a VR experience. For example, bringing up the 2D calculator app in VR.
*/
-class CompatibilityDisplay {
- private final static String TAG = "CompatDisplay";
+class Vr2dDisplay {
+ private final static String TAG = "Vr2dDisplay";
private final static boolean DEBUG = false;
// TODO: Go over these values and figure out what is best
@@ -42,13 +42,13 @@
private final static int STOP_VIRTUAL_DISPLAY_DELAY_MILLIS = 2000;
private final static String DEBUG_ACTION_SET_MODE =
- "com.android.server.vr.CompatibilityDisplay.SET_MODE";
+ "com.android.server.vr.Vr2dDisplay.SET_MODE";
private final static String DEBUG_EXTRA_MODE_ON =
- "com.android.server.vr.CompatibilityDisplay.EXTRA_MODE_ON";
+ "com.android.server.vr.Vr2dDisplay.EXTRA_MODE_ON";
private final static String DEBUG_ACTION_SET_SURFACE =
- "com.android.server.vr.CompatibilityDisplay.SET_SURFACE";
+ "com.android.server.vr.Vr2dDisplay.SET_SURFACE";
private final static String DEBUG_EXTRA_SURFACE =
- "com.android.server.vr.CompatibilityDisplay.EXTRA_SURFACE";
+ "com.android.server.vr.Vr2dDisplay.EXTRA_SURFACE";
/**
* The default width of the VR virtual display
@@ -99,7 +99,7 @@
private boolean mIsVrModeOverrideEnabled;
private boolean mIsVrModeEnabled;
- public CompatibilityDisplay(DisplayManager displayManager,
+ public Vr2dDisplay(DisplayManager displayManager,
ActivityManagerInternal activityManagerInternal, IVrManager vrManager) {
mDisplayManager = displayManager;
mActivityManagerInternal = activityManagerInternal;
@@ -190,7 +190,7 @@
}
/**
- * Sets the resolution and DPI of the compatibility virtual display used to display
+ * Sets the resolution and DPI of the Vr2d virtual display used to display
* 2D applications in VR mode.
*
* <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p>
@@ -198,7 +198,7 @@
* @param compatDisplayProperties Properties of the virtual display for 2D applications
* in VR mode.
*/
- public void setVirtualDisplayProperties(CompatibilityDisplayProperties compatDisplayProperties) {
+ public void setVirtualDisplayProperties(Vr2dDisplayProperties compatDisplayProperties) {
synchronized(mVdLock) {
if (DEBUG) {
Log.i(TAG, "VD setVirtualDisplayProperties: res = " +
@@ -273,13 +273,13 @@
null /* Surface */, 0 /* flags */);
if (mVirtualDisplay != null) {
- mActivityManagerInternal.setVrCompatibilityDisplayId(
+ mActivityManagerInternal.setVr2dDisplayId(
mVirtualDisplay.getDisplay().getDisplayId());
// Now create the ImageReader to supply a Surface to the new virtual display.
startImageReader();
} else {
Log.w(TAG, "Virtual display id is null after createVirtualDisplay");
- mActivityManagerInternal.setVrCompatibilityDisplayId(INVALID_DISPLAY);
+ mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY);
return;
}
}
@@ -302,7 +302,7 @@
} else {
Log.i(TAG, "Stopping Virtual Display");
synchronized (mVdLock) {
- mActivityManagerInternal.setVrCompatibilityDisplayId(INVALID_DISPLAY);
+ mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY);
setSurfaceLocked(null); // clean up and release the surface first.
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e0fc531..a5615a9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -166,8 +166,11 @@
*/
static final class ActiveForegroundApp {
String mPackageName;
+ int mUid;
CharSequence mLabel;
boolean mShownWhileScreenOn;
+ boolean mAppOnTop;
+ boolean mShownWhileTop;
long mStartTime;
long mStartVisibleTime;
long mEndTime;
@@ -728,11 +731,12 @@
synchronized (mAm) {
final long now = SystemClock.elapsedRealtime();
final long nowPlusMin = now + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
+ long nextUpdateTime = Long.MAX_VALUE;
if (smap != null) {
for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i);
if (aa.mEndTime != 0 && (mScreenOn || aa.mShownWhileScreenOn)) {
- if (aa.mEndTime < (aa.mStartVisibleTime
+ if (!aa.mShownWhileTop && aa.mEndTime < (aa.mStartVisibleTime
+ mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
// Check to see if this should still be displayed... we continue
// until it has been shown for at least the timeout duration.
@@ -741,6 +745,12 @@
smap.mActiveForegroundApps.removeAt(i);
smap.mActiveForegroundAppsChanged = true;
continue;
+ } else {
+ long hideTime = aa.mStartVisibleTime
+ + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
+ if (hideTime < nextUpdateTime) {
+ nextUpdateTime = hideTime;
+ }
}
} else {
// This was up for longer than the timeout, so just remove immediately.
@@ -749,10 +759,17 @@
continue;
}
}
- if (active == null) {
- active = new ArrayList<>();
+ if (!aa.mAppOnTop) {
+ if (active == null) {
+ active = new ArrayList<>();
+ }
+ active.add(aa);
}
- active.add(aa);
+ }
+ smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
+ if (nextUpdateTime < Long.MAX_VALUE) {
+ Message msg = smap.obtainMessage();
+ smap.sendMessageAtTime(msg, nextUpdateTime);
}
}
if (!smap.mActiveForegroundAppsChanged) {
@@ -842,7 +859,7 @@
active.mNumActive--;
if (active.mNumActive <= 0) {
active.mEndTime = SystemClock.elapsedRealtime();
- if (active.mEndTime >= (active.mStartVisibleTime
+ if (active.mShownWhileTop || active.mEndTime >= (active.mStartVisibleTime
+ mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
// Have been active for long enough that we will remove it immediately.
smap.mActiveForegroundApps.remove(r.packageName);
@@ -887,6 +904,31 @@
}
}
+ void foregroundServiceProcStateChangedLocked(UidRecord uidRec) {
+ ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.uid));
+ if (smap != null) {
+ boolean changed = false;
+ for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
+ ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
+ if (active.mUid == uidRec.uid) {
+ if (uidRec.curProcState <= ActivityManager.PROCESS_STATE_TOP) {
+ if (!active.mAppOnTop) {
+ active.mAppOnTop = true;
+ changed = true;
+ }
+ active.mShownWhileTop = true;
+ } else if (active.mAppOnTop) {
+ active.mAppOnTop = false;
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ requestUpdateActiveForegroundAppsLocked(smap, 0);
+ }
+ }
+ }
+
private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
Notification notification, int flags) {
if (id != 0) {
@@ -948,7 +990,13 @@
if (active == null) {
active = new ActiveForegroundApp();
active.mPackageName = r.packageName;
+ active.mUid = r.appInfo.uid;
active.mShownWhileScreenOn = mScreenOn;
+ if (r.app != null) {
+ active.mAppOnTop = active.mShownWhileTop =
+ r.app.uidRecord.curProcState
+ <= ActivityManager.PROCESS_STATE_TOP;
+ }
active.mStartTime = active.mStartVisibleTime
= SystemClock.elapsedRealtime();
smap.mActiveForegroundApps.put(r.packageName, active);
@@ -1258,6 +1306,10 @@
mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,
s.appInfo.uid, s.name, s.processName);
+ // Once the apps have become associated, if one of them is caller is ephemeral
+ // the target app should now be able to see the calling app
+ mAm.grantEphemeralAccessLocked(callerApp.userId, service,
+ s.appInfo.uid, UserHandle.getAppId(callerApp.uid));
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
@@ -2786,6 +2838,9 @@
if (!doit && didSomething) {
return true;
}
+ if (doit && filterByClasses == null) {
+ forceStopPackageLocked(packageName, mServiceMap.valueAt(i).mUserId);
+ }
}
} else {
ServiceMap smap = mServiceMap.get(userId);
@@ -2794,6 +2849,9 @@
didSomething = collectPackageServicesLocked(packageName, filterByClasses,
evenPersistent, doit, killProcess, items);
}
+ if (doit && filterByClasses == null) {
+ forceStopPackageLocked(packageName, userId);
+ }
}
if (mTmpCollectionResults != null) {
@@ -2802,10 +2860,11 @@
}
mTmpCollectionResults.clear();
}
+
return didSomething;
}
- void removeUninstalledPackageLocked(String packageName, int userId) {
+ void forceStopPackageLocked(String packageName, int userId) {
ServiceMap smap = mServiceMap.get(userId);
if (smap != null && smap.mActiveForegroundApps.size() > 0) {
for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
@@ -3636,6 +3695,10 @@
}
pw.print(" mNumActive=");
pw.print(aa.mNumActive);
+ pw.print(" mAppOnTop=");
+ pw.print(aa.mAppOnTop);
+ pw.print(" mShownWhileTop=");
+ pw.print(aa.mShownWhileTop);
pw.print(" mShownWhileScreenOn=");
pw.println(aa.mShownWhileScreenOn);
pw.print(" mStartTime=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ace2b55..1ac7f72 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -120,7 +120,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
@@ -572,11 +571,10 @@
// Determines whether to take full screen screenshots
static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
- // STOPSHIP: Update default to a smaller value.
/**
* Default value for {@link Settings.Global#NETWORK_ACCESS_TIMEOUT_MS}.
*/
- private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 2000; // 2 sec
+ private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 200; // 0.2 sec
/**
* State indicating that there is no need for any blocking for network.
@@ -627,8 +625,8 @@
private final VrController mVrController;
- // VR Compatibility Display Id.
- int mVrCompatibilityDisplayId = INVALID_DISPLAY;
+ // VR Vr2d Display Id.
+ int mVr2dDisplayId = INVALID_DISPLAY;
// Whether we should use SCHED_FIFO for UI and RenderThreads.
private boolean mUseFifoUiScheduling = false;
@@ -4312,6 +4310,7 @@
validateUid.idle = false;
}
validateUid.curProcState = validateUid.setProcState = item.processState;
+ validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
}
@@ -5407,7 +5406,7 @@
public static class DumpStackFileObserver extends FileObserver {
// Keep in sync with frameworks/native/cmds/dumpstate/utils.cpp
private static final int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
- static final int TRACE_DUMP_TIMEOUT_SECONDS = TRACE_DUMP_TIMEOUT_MS / 1000;
+ static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
private final String mTracesPath;
private boolean mClosed;
@@ -5423,21 +5422,41 @@
notify();
}
- public void dumpWithTimeout(int pid) {
+ public long dumpWithTimeout(int pid, long timeout) {
sendSignal(pid, SIGNAL_QUIT);
+ final long start = SystemClock.elapsedRealtime();
+
+ final long waitTime = Math.min(timeout, TRACE_DUMP_TIMEOUT_MS);
synchronized (this) {
try {
- wait(TRACE_DUMP_TIMEOUT_MS); // Wait for traces file to be closed.
+ wait(waitTime); // Wait for traces file to be closed.
} catch (InterruptedException e) {
Slog.wtf(TAG, e);
}
}
+
+ // This avoids a corner case of passing a negative time to the native
+ // trace in case we've already hit the overall timeout.
+ final long timeWaited = SystemClock.elapsedRealtime() - start;
+ if (timeWaited >= timeout) {
+ return timeWaited;
+ }
+
if (!mClosed) {
Slog.w(TAG, "Didn't see close of " + mTracesPath + " for pid " + pid +
". Attempting native stack collection.");
- Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath, TRACE_DUMP_TIMEOUT_SECONDS);
+
+ final long nativeDumpTimeoutMs = Math.min(
+ NATIVE_DUMP_TIMEOUT_MS, timeout - timeWaited);
+
+ Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath,
+ (int) (nativeDumpTimeoutMs / 1000));
}
+
+ final long end = SystemClock.elapsedRealtime();
mClosed = false;
+
+ return (end - start);
}
}
@@ -5447,6 +5466,9 @@
// Use a FileObserver to detect when traces finish writing.
// The order of traces is considered important to maintain for legibility.
DumpStackFileObserver observer = new DumpStackFileObserver(tracesPath);
+
+ // We must complete all stack dumps within 20 seconds.
+ long remainingTime = 20 * 1000;
try {
observer.startWatching();
@@ -5456,10 +5478,18 @@
for (int i = 0; i < num; i++) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
+ firstPids.get(i));
- final long sime = SystemClock.elapsedRealtime();
- observer.dumpWithTimeout(firstPids.get(i));
- if (DEBUG_ANR) Slog.d(TAG, "Done with pid " + firstPids.get(i)
- + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
+ final long timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
+ }
}
}
@@ -5467,12 +5497,24 @@
if (nativePids != null) {
for (int pid : nativePids) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
- final long sime = SystemClock.elapsedRealtime();
+ final long nativeDumpTimeoutMs = Math.min(
+ DumpStackFileObserver.NATIVE_DUMP_TIMEOUT_MS, remainingTime);
+ final long start = SystemClock.elapsedRealtime();
Debug.dumpNativeBacktraceToFileTimeout(
- pid, tracesPath, DumpStackFileObserver.TRACE_DUMP_TIMEOUT_SECONDS);
- if (DEBUG_ANR) Slog.d(TAG, "Done with native pid " + pid
- + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
+ pid, tracesPath, (int) (nativeDumpTimeoutMs / 1000));
+ final long timeTaken = SystemClock.elapsedRealtime() - start;
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
+ }
}
}
@@ -5496,12 +5538,20 @@
ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
if (lastPids.indexOfKey(stats.pid) >= 0) {
numProcs++;
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid "
- + stats.pid);
- final long stime = SystemClock.elapsedRealtime();
- observer.dumpWithTimeout(stats.pid);
- if (DEBUG_ANR) Slog.d(TAG, "Done with extra pid " + stats.pid
- + " in " + (SystemClock.elapsedRealtime()-stime) + "ms");
+
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+
+ final long timeTaken = observer.dumpWithTimeout(stats.pid, remainingTime);
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + stats.pid +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with extra pid " + stats.pid + " in " + timeTaken + "ms");
+ }
} else if (DEBUG_ANR) {
Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
+ stats.pid);
@@ -18982,7 +19032,7 @@
removeTasksByPackageNameLocked(ssp, userId);
- mServices.removeUninstalledPackageLocked(ssp, userId);
+ mServices.forceStopPackageLocked(ssp, userId);
// Hide the "unsupported display" dialog if necessary.
if (mUnsupportedDisplaySizeDialog != null && ssp.equals(
@@ -19865,8 +19915,9 @@
}
/**
- * NOTE: For the pinned stack, this method is only called after the bounds animation has
- * animated the stack to the fullscreen.
+ * NOTE: For the pinned stack, this method is usually called after the bounds animation has
+ * animated the stack to the fullscreen, but can also be called if we are relaunching an
+ * activity and clearing the task at the same time.
*/
@Override
public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
@@ -22252,6 +22303,9 @@
if (uidRec.curProcState > app.curProcState) {
uidRec.curProcState = app.curProcState;
}
+ if (app.foregroundServices) {
+ uidRec.foregroundServices = true;
+ }
}
}
@@ -22493,6 +22547,9 @@
uidRec.setWhitelist = uidRec.curWhitelist;
enqueueUidChangeLocked(uidRec, -1, uidChange);
noteUidProcessState(uidRec.uid, uidRec.curProcState);
+ if (uidRec.foregroundServices) {
+ mServices.foregroundServiceProcStateChangedLocked(uidRec);
+ }
}
}
if (mLocalPowerManager != null) {
@@ -23701,17 +23758,17 @@
/**
* Called after virtual display Id is updated by
- * {@link com.android.server.vr.CompatibilityDisplay} with a specific
- * {@param vrCompatibilityDisplayId}.
+ * {@link com.android.server.vr.Vr2dDisplay} with a specific
+ * {@param vrVr2dDisplayId}.
*/
@Override
- public void setVrCompatibilityDisplayId(int vrCompatibilityDisplayId) {
+ public void setVr2dDisplayId(int vr2dDisplayId) {
if (DEBUG_STACK) {
- Slog.d(TAG, "setVrCompatibilityDisplayId called for: " +
- vrCompatibilityDisplayId);
+ Slog.d(TAG, "setVr2dDisplayId called for: " +
+ vr2dDisplayId);
}
synchronized (ActivityManagerService.this) {
- mVrCompatibilityDisplayId = vrCompatibilityDisplayId;
+ mVr2dDisplayId = vr2dDisplayId;
}
}
}
@@ -23775,11 +23832,8 @@
if (totalTime >= mWaitForNetworkTimeoutMs) {
Slog.wtf(TAG_NETWORK, "Total time waited for network rules to get updated: "
+ totalTime + ". Uid: " + callingUid + " procStateSeq: "
- + procStateSeq);
- } else if (DEBUG_NETWORK || totalTime >= mWaitForNetworkTimeoutMs / 2) {
- Slog.d(TAG_NETWORK, "Total time waited for network rules to get updated: "
- + totalTime + ". Uid: " + callingUid + " procStateSeq: "
- + procStateSeq);
+ + procStateSeq + " UidRec: " + record
+ + " validateUidRec: " + mValidateUids.get(callingUid));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d17309f..4ad0663 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1659,21 +1659,13 @@
}
if (mStackId == DOCKED_STACK_ID) {
- final ActivityRecord r = topStack.topRunningActivityLocked();
-
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isAssistantStack()) {
return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
: STACK_INVISIBLE;
}
-
- // Otherwise, the docked stack is always visible, except in the case where the top
- // running activity task in the focus stack doesn't support any form of resizing but we
- // show it for the home task even though it's not resizable.
- final TaskRecord task = r != null ? r.getTask() : null;
- return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE
- : STACK_INVISIBLE;
+ return STACK_VISIBLE;
}
// Set home stack to invisible when it is below but not immediately below the docked stack
@@ -1692,14 +1684,17 @@
mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
stackBehindTopIndex--;
}
- if ((topStackId == DOCKED_STACK_ID || topStackId == PINNED_STACK_ID)
- && stackIndex == stackBehindTopIndex) {
- // Stacks directly behind the docked or pinned stack are always visible.
- return STACK_VISIBLE;
- }
-
final int stackBehindTopId = (stackBehindTopIndex >= 0)
? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
+ if ((topStackId == DOCKED_STACK_ID || topStackId == PINNED_STACK_ID)
+ && (stackIndex == stackBehindTopIndex
+ || (stackBehindTopId == DOCKED_STACK_ID
+ && stackIndex == stackBehindTopIndex - 1))) {
+ // Stacks directly behind the docked or pinned stack are always visible.
+ // Also this stack is visible if behind docked stack and the docked stack is behind the
+ // top-most pinned stack
+ return STACK_VISIBLE;
+ }
if (StackId.isBackdropToTranslucentActivity(topStackId)
&& topStack.isStackTranslucent(starting, stackBehindTopId)) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 19b9b45..1f1aa8e 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -186,7 +186,7 @@
private IVoiceInteractionSession mVoiceSession;
private IVoiceInteractor mVoiceInteractor;
- private boolean mUsingVrCompatibilityDisplay;
+ private boolean mUsingVr2dDisplay;
private void reset() {
mStartActivity = null;
@@ -226,14 +226,14 @@
mVoiceSession = null;
mVoiceInteractor = null;
- mUsingVrCompatibilityDisplay = false;
+ mUsingVr2dDisplay = false;
}
ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
mService = service;
mSupervisor = supervisor;
mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
- mUsingVrCompatibilityDisplay = false;
+ mUsingVr2dDisplay = false;
}
final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
@@ -575,12 +575,15 @@
return;
}
- if (startedActivityStackId == PINNED_STACK_ID
- && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP)) {
+ boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+ == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+ if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT
+ || result == START_DELIVERED_TO_TOP || clearedTask)) {
// The activity was already running in the pinned stack so it wasn't started, but either
// brought to the front or the new intent was delivered to it since it was already in
// front. Notify anyone interested in this piece of information.
- mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt();
+ mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
+ clearedTask);
return;
}
}
@@ -953,7 +956,7 @@
// If we are not able to proceed, disassociate the activity from the task. Leaving an
// activity in an incomplete state can lead to issues, such as performing operations
// without a window container.
- if (result != START_SUCCESS && mStartActivity.getTask() != null) {
+ if (result < START_SUCCESS && mStartActivity.getTask() != null) {
mStartActivity.getTask().removeActivity(mStartActivity);
}
mService.mWindowManager.continueSurfaceLayout();
@@ -1059,7 +1062,7 @@
// We didn't do anything... but it was needed (a.k.a., client don't use that
// intent!) And for paranoia, make sure we have correctly resumed the top activity.
resumeTargetStackIfNeeded();
- if (outActivity.length > 0) {
+ if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return START_TASK_TO_FRONT;
@@ -1476,12 +1479,12 @@
}
// Get the virtual display id from ActivityManagerService.
- int displayId = mService.mVrCompatibilityDisplayId;
+ int displayId = mService.mVr2dDisplayId;
if (displayId != INVALID_DISPLAY) {
if (DEBUG_STACK) {
Slog.d(TAG, "getSourceDisplayId :" + displayId);
}
- mUsingVrCompatibilityDisplay = true;
+ mUsingVr2dDisplay = true;
return displayId;
}
@@ -2105,8 +2108,8 @@
return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r);
}
- // If we are using Vr compatibility display, find the virtual display stack.
- if (mUsingVrCompatibilityDisplay) {
+ // If we are using Vr2d display, find the virtual display stack.
+ if (mUsingVr2dDisplay) {
ActivityStack as = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
if (DEBUG_STACK) {
Slog.v(TAG, "Launch stack for app: " + r.toString() +
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b025385..fbc2bd2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -103,7 +103,6 @@
int renderThreadTid; // TID for RenderThread
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
- boolean setIsForeground; // Running foreground UI when last set?
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
boolean hasClientActivities; // Are there any client services with activities?
boolean hasStartedServices; // Are there any started services running in this process?
@@ -303,9 +302,8 @@
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
- if (setIsForeground || foregroundServices || forcingToForeground != null) {
- pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground);
- pw.print(" foregroundServices="); pw.print(foregroundServices);
+ if (foregroundServices || forcingToForeground != null) {
+ pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
pw.print(" forcingToForeground="); pw.println(forcingToForeground);
}
if (reportedInteraction || fgInteractionTime != 0) {
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 7d2bc5b..f5d7b68 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -104,7 +104,7 @@
};
private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
- l.onPinnedActivityRestartAttempt();
+ l.onPinnedActivityRestartAttempt(m.arg1 != 0);
};
private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
@@ -300,10 +300,11 @@
* running in the pinned stack and the activity was not actually started, but the task is
* either brought to the front or a new Intent is delivered to it.
*/
- void notifyPinnedActivityRestartAttempt() {
+ void notifyPinnedActivityRestartAttempt(boolean clearedTask) {
mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
final Message msg =
- mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+ mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
+ clearedTask ? 1 : 0, 0);
forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index c0fb77f..c411bce 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -35,6 +35,7 @@
int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
long lastBackgroundTime;
boolean ephemeral;
+ boolean foregroundServices;
boolean curWhitelist;
boolean setWhitelist;
boolean idle;
@@ -102,6 +103,7 @@
public void reset() {
curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ foregroundServices = false;
}
public void updateHasInternetPermission() {
@@ -131,6 +133,9 @@
if (ephemeral) {
sb.append(" ephemeral");
}
+ if (foregroundServices) {
+ sb.append(" fgServices");
+ }
if (curWhitelist) {
sb.append(" whitelist");
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index d35104f..3053879 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -18,12 +18,10 @@
import android.annotation.NonNull;
import android.media.AudioAttributes;
-import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioSystem;
import android.media.IPlaybackConfigDispatcher;
-import android.media.MediaRecorder;
import android.media.PlayerBase;
import android.media.VolumeShaper;
import android.os.Binder;
@@ -99,13 +97,6 @@
apc.init();
synchronized(mPlayerLock) {
mPlayers.put(newPiid, apc);
- if (mDuckedUids.contains(new Integer(apc.getClientUid()))) {
- if (DEBUG) { Log.v(TAG, " > trackPlayer() piid=" + newPiid + " must be ducked"); }
- mDuckedPlayers.add(new Integer(newPiid));
- // FIXME here the player needs to be put in a state that is the same as if it
- // had been ducked as it starts. At the moment, this works already for linked
- // players, as is the case in gapless playback.
- }
}
return newPiid;
}
@@ -131,10 +122,11 @@
final boolean change;
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
- // FIXME SoundPool not ready for state reporting
- if (apc != null
- && apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL)
- {
+ if (apc == null) {
+ return;
+ }
+ if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ // FIXME SoundPool not ready for state reporting
return;
}
if (checkConfigurationCaller(piid, apc, binderUid)) {
@@ -144,12 +136,8 @@
Log.e(TAG, "Error handling event " + event);
change = false;
}
- if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
- && mDuckedUids.contains(new Integer(apc.getClientUid()))) {
- if (DEBUG) { Log.v(TAG, " > playerEvent() piid=" + piid + " must be ducked"); }
- if (!mDuckedPlayers.contains(new Integer(piid))) {
- mDuckedPlayers.add(new Integer(piid));
- }
+ if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ mDuckingManager.checkDuck(apc);
}
}
if (change) {
@@ -163,6 +151,7 @@
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
mPlayers.remove(new Integer(piid));
+ mDuckingManager.removeReleased(apc);
}
}
}
@@ -182,10 +171,8 @@
conf.dump(pw);
}
// ducked players
- pw.println("\n ducked player piids:");
- for (int piid : mDuckedPlayers) {
- pw.println(" " + piid);
- }
+ pw.println("\n ducked players:");
+ mDuckingManager.dump(pw);
// players muted due to the device ringing or being in a call
pw.println("\n muted player piids:");
for (int piid : mMutedPlayers) {
@@ -274,10 +261,9 @@
//=================================================================
// PlayerFocusEnforcer implementation
- private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
- // size of 2 for typical cases of double-ducking, not expected to grow beyond that, but can
- private final ArrayList<Integer> mDuckedUids = new ArrayList<Integer>(2);
+
+ private final DuckingManager mDuckingManager = new DuckingManager();
@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
@@ -286,60 +272,46 @@
winner.getClientUid(), loser.getClientUid()));
}
synchronized (mPlayerLock) {
- final Integer loserUid = new Integer(loser.getClientUid());
- if (!mDuckedUids.contains(loserUid)) {
- mDuckedUids.add(loserUid);
- }
if (mPlayers.isEmpty()) {
return true;
}
- final Set<Integer> piidSet = mPlayers.keySet();
- final Iterator<Integer> piidIterator = piidSet.iterator();
- // find which players to duck
- while (piidIterator.hasNext()) {
- final Integer piid = piidIterator.next();
- final AudioPlaybackConfiguration apc = mPlayers.get(piid);
- if (apc == null) {
- continue;
- }
+ // check if this UID needs to be ducked (return false if not), and gather list of
+ // eligible players to duck
+ final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
+ final ArrayList<AudioPlaybackConfiguration> apcsToDuck =
+ new ArrayList<AudioPlaybackConfiguration>();
+ while (apcIterator.hasNext()) {
+ final AudioPlaybackConfiguration apc = apcIterator.next();
if (!winner.hasSameUid(apc.getClientUid())
&& loser.hasSameUid(apc.getClientUid())
&& apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
{
- if (mDuckedPlayers.contains(new Integer(piid))) {
- if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); }
- } else if (MediaFocusControl.ENFORCE_DUCKING
+ if (MediaFocusControl.ENFORCE_DUCKING
&& MediaFocusControl.ENFORCE_DUCKING_FOR_NEW
&& loser.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL) {
// legacy behavior, apps used to be notified when they should be ducking
- if (DEBUG) { Log.v(TAG, "not ducking player " + piid + ": old SDK"); }
+ if (DEBUG) {Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
+ + ": old SDK"); }
return false;
} else if (apc.getAudioAttributes().getContentType() ==
AudioAttributes.CONTENT_TYPE_SPEECH) {
// the player is speaking, ducking will make the speech unintelligible
// so let the app handle it instead
- if (DEBUG) { Log.v(TAG, "not ducking player " + piid + ": SPEECH"); }
+ if (DEBUG) { Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
+ + ": SPEECH"); }
return false;
} else if (apc.getPlayerType()
== AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
// TODO support ducking of SoundPool players
return false;
- } else {
- try {
- Log.v(TAG, "ducking player " + piid);
- apc.getPlayerProxy().applyVolumeShaper(
- DUCK_VSHAPE,
- PLAY_CREATE_IF_NEEDED);
- mDuckedPlayers.add(new Integer(piid));
- } catch (Exception e) {
- Log.e(TAG, "Error ducking player " + piid, e);
- // something went wrong trying to duck, so let the app handle it
- // instead, it may know things we don't
- return false;
- }
}
+ apcsToDuck.add(apc);
}
}
+ // add the players eligible for ducking to the list, and duck them
+ // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when
+ // players of the same uid start, they will be ducked by DuckingManager.checkDuck())
+ mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);
}
return true;
}
@@ -348,37 +320,7 @@
public void unduckPlayers(FocusRequester winner) {
if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
synchronized (mPlayerLock) {
- if (mDuckedPlayers.isEmpty()) {
- mDuckedUids.remove(new Integer(winner.getClientUid()));
- return;
- }
- final ArrayList<Integer> playersToRemove =
- new ArrayList<Integer>(mDuckedPlayers.size());
- for (int piid : mDuckedPlayers) {
- final AudioPlaybackConfiguration apc = mPlayers.get(piid);
- if (apc != null) {
- if (winner.hasSameUid(apc.getClientUid())) {
- try {
- Log.v(TAG, "unducking player " + piid);
- apc.getPlayerProxy().applyVolumeShaper(
- DUCK_ID,
- VolumeShaper.Operation.REVERSE);
- } catch (Exception e) {
- Log.e(TAG, "Error unducking player " + piid, e);
- } finally {
- playersToRemove.add(piid);
- }
- }
- } else {
- // this piid was in the list of ducked players, but wasn't found, discard it
- Log.v(TAG, "Error unducking player " + piid + ", player not found");
- playersToRemove.add(piid);
- }
- }
- for (int piid : playersToRemove) {
- mDuckedPlayers.remove(new Integer(piid));
- }
- mDuckedUids.remove(new Integer(winner.getClientUid()));
+ mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
}
}
@@ -541,4 +483,118 @@
mDispatcherCb.asBinder().unlinkToDeath(this, 0);
}
}
+
+ //=================================================================
+ // Class to handle ducking related operations for a given UID
+ private static final class DuckingManager {
+ private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
+
+ void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
+ if (!mDuckers.containsKey(uid)) {
+ mDuckers.put(uid, new DuckedApp(uid));
+ }
+ final DuckedApp da = mDuckers.get(uid);
+ for (AudioPlaybackConfiguration apc : apcsToDuck) {
+ da.addDuck(apc);
+ }
+ }
+
+ void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
+ final DuckedApp da = mDuckers.remove(uid);
+ if (da == null) {
+ return;
+ }
+ da.removeUnduckAll(players);
+ }
+
+ // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+ void checkDuck(@NonNull AudioPlaybackConfiguration apc) {
+ final DuckedApp da = mDuckers.get(apc.getClientUid());
+ if (da == null) {
+ return;
+ }
+ // FIXME here the player needs to be put in a state that is the same as if it
+ // had been ducked as it starts. At the moment, this works already for linked
+ // players, as is the case in gapless playback.
+ da.addDuck(apc);
+ }
+
+ void dump(PrintWriter pw) {
+ for (DuckedApp da : mDuckers.values()) {
+ da.dump(pw);
+ }
+ }
+
+ void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+ final DuckedApp da = mDuckers.get(apc.getClientUid());
+ if (da == null) {
+ return;
+ }
+ da.removeReleased(apc);
+ }
+
+ private static final class DuckedApp {
+ private final int mUid;
+ private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
+
+ DuckedApp(int uid) {
+ mUid = uid;
+ }
+
+ void dump(PrintWriter pw) {
+ pw.print("\t uid:" + mUid + " piids:");
+ for (int piid : mDuckedPlayers) {
+ pw.print(" " + piid);
+ }
+ pw.println("");
+ }
+
+ // pre-conditions:
+ // * apc != null
+ // * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+ void addDuck(@NonNull AudioPlaybackConfiguration apc) {
+ final int piid = new Integer(apc.getPlayerInterfaceId());
+ if (mDuckedPlayers.contains(piid)) {
+ if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); }
+ return;
+ }
+ try {
+ Log.v(TAG, "ducking player " + apc.getPlayerInterfaceId());
+ apc.getPlayerProxy().applyVolumeShaper(
+ DUCK_VSHAPE,
+ PLAY_CREATE_IF_NEEDED);
+ mDuckedPlayers.add(piid);
+ } catch (Exception e) {
+ Log.e(TAG, "Error ducking player " + piid, e);
+ }
+ }
+
+ void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
+ for (int piid : mDuckedPlayers) {
+ final AudioPlaybackConfiguration apc = players.get(piid);
+ if (apc != null) {
+ try {
+ Log.v(TAG, "unducking player " + piid);
+ apc.getPlayerProxy().applyVolumeShaper(
+ DUCK_ID,
+ VolumeShaper.Operation.REVERSE);
+ } catch (Exception e) {
+ Log.e(TAG, "Error unducking player " + piid, e);
+ }
+ } else {
+ // this piid was in the list of ducked players, but wasn't found
+ if (DEBUG) {
+ Log.v(TAG, "Error unducking player " + piid + ", player not found for"
+ + " uid " + mUid);
+ }
+ }
+ }
+ mDuckedPlayers.clear();
+ }
+
+ void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+ mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
index 6610634..88a6bdd 100644
--- a/services/core/java/com/android/server/fingerprint/RemovalClient.java
+++ b/services/core/java/com/android/server/fingerprint/RemovalClient.java
@@ -91,7 +91,7 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Removed:", e);
}
- return fingerId == 0;
+ return remaining == 0;
}
@Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index aafc9a8..0e52871 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -230,6 +230,9 @@
private static native void nativeReloadDeviceAliases(long ptr);
private static native String nativeDump(long ptr);
private static native void nativeMonitor(long ptr);
+ private static native boolean nativeIsInputDeviceEnabled(long ptr, int deviceId);
+ private static native void nativeEnableInputDevice(long ptr, int deviceId);
+ private static native void nativeDisableInputDevice(long ptr, int deviceId);
private static native void nativeSetPointerIconType(long ptr, int iconId);
private static native void nativeReloadPointerIcons(long ptr);
private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
@@ -639,6 +642,32 @@
return null;
}
+ // Binder call
+ @Override
+ public boolean isInputDeviceEnabled(int deviceId) {
+ return nativeIsInputDeviceEnabled(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
+ public void enableInputDevice(int deviceId) {
+ if (!checkCallingPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE,
+ "enableInputDevice()")) {
+ throw new SecurityException("Requires DISABLE_INPUT_DEVICE permission");
+ }
+ nativeEnableInputDevice(mPtr, deviceId);
+ }
+
+ // Binder call
+ @Override
+ public void disableInputDevice(int deviceId) {
+ if (!checkCallingPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE,
+ "disableInputDevice()")) {
+ throw new SecurityException("Requires DISABLE_INPUT_DEVICE permission");
+ }
+ nativeDisableInputDevice(mPtr, deviceId);
+ }
+
/**
* Gets the ids of all input devices in the system.
* @return The input device ids.
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9620544..4b632fe 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -384,6 +384,8 @@
// Wakelocks
private final static String WAKELOCK_KEY = "GnssLocationProvider";
private final PowerManager.WakeLock mWakeLock;
+ private static final String DOWNLOAD_EXTRA_WAKELOCK_KEY = "GnssLocationProviderXtraDownload";
+ private final PowerManager.WakeLock mDownloadXtraWakeLock;
// Alarms
private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
@@ -691,6 +693,11 @@
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
mWakeLock.setReferenceCounted(true);
+ // Create a separate wake lock for xtra downloader as it may be released due to timeout.
+ mDownloadXtraWakeLock = mPowerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, DOWNLOAD_EXTRA_WAKELOCK_KEY);
+ mDownloadXtraWakeLock.setReferenceCounted(true);
+
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
@@ -995,7 +1002,7 @@
mDownloadXtraDataPending = STATE_DOWNLOADING;
// hold wake lock while task runs
- mWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
+ mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
@@ -1017,13 +1024,17 @@
mXtraBackOff.nextBackoffMillis());
}
- // release wake lock held by task
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- } else {
- Log.e(TAG, "WakeLock expired before release in handleDownloadXtraData()");
+ // Release wake lock held by task, synchronize on mLock in case multiple
+ // download tasks overrun.
+ synchronized (mLock) {
+ if (mDownloadXtraWakeLock.isHeld()) {
+ mDownloadXtraWakeLock.release();
+ if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+ } else {
+ Log.e(TAG, "WakeLock expired before release in "
+ + "handleDownloadXtraData()");
+ }
}
- Log.i(TAG, "WakeLock released by handleDownloadXtraData()");
}
});
}
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index f92bf3d..4981d5c 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -66,7 +66,11 @@
@Override
public void applyChangesLocked(NotificationRecord record) {
- record.setRecentlyIntrusive(false);
+ // there will be another reconsideration in the message queue HANG_TIME_MS
+ // from each time this record alerts, which can finally clear this flag.
+ if ((System.currentTimeMillis() - record.getLastIntrusive()) >= HANG_TIME_MS) {
+ record.setRecentlyIntrusive(false);
+ }
}
};
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 48d11c3..1d843c1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -884,6 +884,8 @@
} else if (action.equals(Intent.ACTION_USER_REMOVED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mZenModeHelper.onUserRemoved(user);
+ mRankingHelper.onUserRemoved(user);
+ savePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mConditionProviders.onUserUnlocked(user);
@@ -3199,10 +3201,8 @@
+ ", incomingUserId=" + incomingUserId
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
- // STOPSHIP TODO: should throw instead of logging or toasting.
- // throw new IllegalArgumentException(noChannelStr);
Log.e(TAG, noChannelStr);
- doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" +
+ doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
"Failed to post notification on channel \"" + channelId + "\"\n" +
"See log for more details");
return;
@@ -3238,8 +3238,10 @@
mHandler.post(new EnqueueNotificationRunnable(userId, r));
}
- private void doDebugOnlyToast(CharSequence toastText) {
- if (Build.IS_DEBUGGABLE) {
+ private void doChannelWarningToast(CharSequence toastText) {
+ final boolean warningEnabled = Settings.System.getInt(getContext().getContentResolver(),
+ Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 0) != 0;
+ if (warningEnabled || Build.IS_DEBUGGABLE) {
try {
Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG);
toast.show();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 90257da..f019a5c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -85,6 +85,7 @@
// to communicate with the ranking module.
private float mContactAffinity;
private boolean mRecentlyIntrusive;
+ private long mLastIntrusive;
// is this notification currently being intercepted by Zen Mode?
private boolean mIntercept;
@@ -515,12 +516,19 @@
public void setRecentlyIntrusive(boolean recentlyIntrusive) {
mRecentlyIntrusive = recentlyIntrusive;
+ if (recentlyIntrusive) {
+ mLastIntrusive = System.currentTimeMillis();
+ }
}
public boolean isRecentlyIntrusive() {
return mRecentlyIntrusive;
}
+ public long getLastIntrusive() {
+ return mLastIntrusive;
+ }
+
public void setPackagePriority(int packagePriority) {
mPackagePriority = packagePriority;
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3481556..1e741de 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -1040,6 +1040,18 @@
return packageChannels;
}
+ public void onUserRemoved(int userId) {
+ synchronized (mRecords) {
+ int N = mRecords.size();
+ for (int i = N - 1; i >= 0 ; i--) {
+ Record record = mRecords.valueAt(i);
+ if (UserHandle.getUserId(record.uid) == userId) {
+ mRecords.removeAt(i);
+ }
+ }
+ }
+ }
+
public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
int[] uidList) {
if (pkgList == null || pkgList.length == 0) {
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 9d08004..09e9433 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -43,6 +43,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.concurrent.TimeoutException;
/**
@@ -206,7 +207,9 @@
private void handleBinderDiedLocked() {
if (mRemoteInstance != null) {
- mRemoteInstance.asBinder().unlinkToDeath(this, 0 /*flags*/);
+ try {
+ mRemoteInstance.asBinder().unlinkToDeath(this, 0 /*flags*/);
+ } catch (NoSuchElementException ignore) { }
}
mRemoteInstance = null;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4c0d9da..8ca7b96 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -24,6 +24,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Log;
@@ -54,7 +55,10 @@
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
+
+import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
+import static dalvik.system.DexFile.getSafeModeCompilerFilter;
import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
/**
@@ -67,13 +71,17 @@
public static final int DEX_OPT_SKIPPED = 0;
public static final int DEX_OPT_PERFORMED = 1;
public static final int DEX_OPT_FAILED = -1;
+ // One minute over PM WATCHDOG_TIMEOUT
+ private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
/** Special library name that skips shared libraries check during compilation. */
public static final String SKIP_SHARED_LIBRARY_CHECK = "&";
+ @GuardedBy("mInstallLock")
private final Installer mInstaller;
private final Object mInstallLock;
+ @GuardedBy("mInstallLock")
private final PowerManager.WakeLock mDexoptWakeLock;
private volatile boolean mSystemReady;
@@ -111,21 +119,12 @@
return DEX_OPT_SKIPPED;
}
synchronized (mInstallLock) {
- // During boot the system doesn't need to instantiate and obtain a wake lock.
- // PowerManager might not be ready, but that doesn't mean that we can't proceed with
- // dexopt.
- final boolean useLock = mSystemReady;
- if (useLock) {
- mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
- mDexoptWakeLock.acquire();
- }
+ final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
try {
return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
targetCompilationFilter, packageStats, isUsedByOtherApps);
} finally {
- if (useLock) {
- mDexoptWakeLock.release();
- }
+ releaseWakeLockLI(acquireTime);
}
}
}
@@ -250,26 +249,50 @@
public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
String compilerFilter, boolean isUsedByOtherApps) {
synchronized (mInstallLock) {
- // During boot the system doesn't need to instantiate and obtain a wake lock.
- // PowerManager might not be ready, but that doesn't mean that we can't proceed with
- // dexopt.
- final boolean useLock = mSystemReady;
- if (useLock) {
- mDexoptWakeLock.setWorkSource(new WorkSource(info.uid));
- mDexoptWakeLock.acquire();
- }
+ final long acquireTime = acquireWakeLockLI(info.uid);
try {
return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
isUsedByOtherApps);
} finally {
- if (useLock) {
- mDexoptWakeLock.release();
- }
+ releaseWakeLockLI(acquireTime);
}
}
}
@GuardedBy("mInstallLock")
+ private long acquireWakeLockLI(final int uid) {
+ // During boot the system doesn't need to instantiate and obtain a wake lock.
+ // PowerManager might not be ready, but that doesn't mean that we can't proceed with
+ // dexopt.
+ if (!mSystemReady) {
+ return -1;
+ }
+ mDexoptWakeLock.setWorkSource(new WorkSource(uid));
+ mDexoptWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ return SystemClock.elapsedRealtime();
+ }
+
+ @GuardedBy("mInstallLock")
+ private void releaseWakeLockLI(final long acquireTime) {
+ if (acquireTime < 0) {
+ return;
+ }
+ try {
+ if (mDexoptWakeLock.isHeld()) {
+ mDexoptWakeLock.release();
+ }
+ final long duration = SystemClock.elapsedRealtime() - acquireTime;
+ if (duration >= WAKELOCK_TIMEOUT_MS) {
+ Slog.wtf(TAG, "WakeLock " + mDexoptWakeLock.getTag()
+ + " time out. Operation took " + duration + " ms. Thread: "
+ + Thread.currentThread().getName());
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
+ }
+ }
+
+ @GuardedBy("mInstallLock")
private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
String compilerFilter, boolean isUsedByOtherApps) {
compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
@@ -360,13 +383,7 @@
int flags = info.flags;
boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
if (vmSafeMode) {
- // For the compilation, it doesn't really matter what we return here because installd
- // will replace the filter with 'quicken' anyway.
- // However, we return a non profile guided filter so that we simplify the logic of
- // merging profiles.
- // TODO(calin): safe mode path could be simplified if we pass 'quicken' from
- // here rather than letting installd decide on the filter.
- return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ return getSafeModeCompilerFilter(targetCompilerFilter);
}
if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
@@ -613,9 +630,13 @@
@Override
protected int adjustDexoptNeeded(int dexoptNeeded) {
- // Ensure compilation, no matter the current state.
- // TODO: The return value is wrong when patchoat is needed.
- return DexFile.DEX2OAT_FROM_SCRATCH;
+ if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
+ // Ensure compilation by pretending a compiler filter change on the
+ // apk/odex location (the reason for the '-'. A positive value means
+ // the 'oat' location).
+ return -DexFile.DEX2OAT_FOR_FILTER;
+ }
+ return dexoptNeeded;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1449aee..6101e5b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -97,11 +97,12 @@
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -445,7 +446,7 @@
* minute but we sometimes do very lengthy I/O operations on this thread,
* such as installing multi-gigabyte applications, so ours needs to be longer.
*/
- private static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes
+ static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes
/**
* Wall-clock timeout (in milliseconds) after which we *require* that an fstrim
@@ -18541,11 +18542,6 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- Log.i(TAG, "Package doesn't exist in set block uninstall " + packageName);
- return false;
- }
// Cannot block uninstall of static shared libs as they are
// considered a part of the using app (emulating static linking).
// Also static libs are installed always on internal storage.
@@ -18555,12 +18551,7 @@
+ " providing static shared library: " + pkg.staticSharedLibName);
return false;
}
- if (!ps.getInstalled(userId)) {
- // Can't block uninstall for an app that is not installed or enabled.
- Log.i(TAG, "Package not installed in set block uninstall " + packageName);
- return false;
- }
- ps.setBlockUninstall(blockUninstall, userId);
+ mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
mSettings.writePackageRestrictionsLPr(userId);
}
return true;
@@ -18569,12 +18560,7 @@
@Override
public boolean getBlockUninstallForUser(String packageName, int userId) {
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- Log.i(TAG, "Package doesn't exist in get block uninstall " + packageName);
- return false;
- }
- return ps.getBlockUninstall(userId);
+ return mSettings.getBlockUninstallLPr(userId, packageName);
}
}
@@ -18781,7 +18767,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- false /*blockUninstall*/,
ps.readUserState(nextUserId).domainVerificationStatus,
0, PackageManager.INSTALL_REASON_UNKNOWN);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 284bb3f..ec248f5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -111,11 +111,4 @@
return value;
}
-
- /**
- * Return the non-profile-guided filter corresponding to the given filter.
- */
- public static String getNonProfileGuidedCompilerFilter(String filter) {
- return DexFile.getNonProfileGuidedCompilerFilter(filter);
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index dfed72f..14f65eb 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -402,14 +402,6 @@
modifyUserState(userId).suspended = suspended;
}
- boolean getBlockUninstall(int userId) {
- return readUserState(userId).blockUninstall;
- }
-
- void setBlockUninstall(boolean blockUninstall, int userId) {
- modifyUserState(userId).blockUninstall = blockUninstall;
- }
-
boolean getInstantApp(int userId) {
return readUserState(userId).instantApp;
}
@@ -421,8 +413,8 @@
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
- ArraySet<String> disabledComponents, boolean blockUninstall,
- int domainVerifState, int linkGeneration, int installReason) {
+ ArraySet<String> disabledComponents, int domainVerifState,
+ int linkGeneration, int installReason) {
PackageUserState state = modifyUserState(userId);
state.ceDataInode = ceDataInode;
state.enabled = enabled;
@@ -434,7 +426,6 @@
state.lastDisableAppCaller = lastDisableAppCaller;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
- state.blockUninstall = blockUninstall;
state.domainVerificationStatus = domainVerifState;
state.appLinkGeneration = linkGeneration;
state.installReason = installReason;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index cea031e..44bcff2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -186,6 +186,8 @@
private static final String TAG_PERMISSIONS = "perms";
private static final String TAG_CHILD_PACKAGE = "child-package";
private static final String TAG_USES_STATIC_LIB = "uses-static-lib";
+ private static final String TAG_BLOCK_UNINSTALL_PACKAGES = "block-uninstall-packages";
+ private static final String TAG_BLOCK_UNINSTALL = "block-uninstall";
private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
"persistent-preferred-activities";
@@ -215,6 +217,8 @@
// New name for the above attribute.
private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_SUSPENDED = "suspended";
+ // Legacy, uninstall blocks are stored separately.
+ @Deprecated
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
private static final String ATTR_ENABLED = "enabled";
private static final String ATTR_ENABLED_CALLER = "enabledCaller";
@@ -271,6 +275,9 @@
private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
new ArrayMap<String, PackageSetting>();
+ /** List of packages that are blocked for uninstall for specific users */
+ private final SparseArray<ArraySet<String>> mBlockUninstallPackages = new SparseArray<>();
+
// Set of restored intent-filter verification states
private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
new ArrayMap<String, IntentFilterVerificationInfo>();
@@ -756,7 +763,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- false /*blockUninstall*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
0, PackageManager.INSTALL_REASON_UNKNOWN);
}
@@ -1614,6 +1620,34 @@
}
}
+ void readBlockUninstallPackagesLPw(XmlPullParser parser, int userId)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ ArraySet<String> packages = new ArraySet<>();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_BLOCK_UNINSTALL)) {
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ packages.add(packageName);
+ } else {
+ String msg = "Unknown element under " + TAG_BLOCK_UNINSTALL_PACKAGES + ": " +
+ parser.getName();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ if (packages.isEmpty()) {
+ mBlockUninstallPackages.remove(userId);
+ } else {
+ mBlockUninstallPackages.put(userId, packages);
+ }
+ }
+
void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1662,7 +1696,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- false /*blockUninstall*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
0, PackageManager.INSTALL_REASON_UNKNOWN);
}
@@ -1768,9 +1801,12 @@
}
}
+ if (blockUninstall) {
+ setBlockUninstallLPw(userId, name, true);
+ }
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, instantApp, enabledCaller, enabledComponents,
- disabledComponents, blockUninstall, verifState, linkGeneration,
+ disabledComponents, verifState, linkGeneration,
installReason);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
@@ -1780,6 +1816,8 @@
readCrossProfileIntentFiltersLPw(parser, userId);
} else if (tagName.equals(TAG_DEFAULT_APPS)) {
readDefaultAppsLPw(parser, userId);
+ } else if (tagName.equals(TAG_BLOCK_UNINSTALL_PACKAGES)) {
+ readBlockUninstallPackagesLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -1806,6 +1844,30 @@
}
}
+ void setBlockUninstallLPw(int userId, String packageName, boolean blockUninstall) {
+ ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+ if (blockUninstall) {
+ if (packages == null) {
+ packages = new ArraySet<String>();
+ mBlockUninstallPackages.put(userId, packages);
+ }
+ packages.add(packageName);
+ } else if (packages != null) {
+ packages.remove(packageName);
+ if (packages.isEmpty()) {
+ mBlockUninstallPackages.remove(userId);
+ }
+ }
+ }
+
+ boolean getBlockUninstallLPr(int userId, String packageName) {
+ ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+ if (packages == null) {
+ return false;
+ }
+ return packages.contains(packageName);
+ }
+
private ArraySet<String> readComponentsLPr(XmlPullParser parser)
throws IOException, XmlPullParserException {
ArraySet<String> components = null;
@@ -1976,6 +2038,20 @@
serializer.endTag(null, TAG_DEFAULT_APPS);
}
+ void writeBlockUninstallPackagesLPr(XmlSerializer serializer, int userId)
+ throws IOException {
+ ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+ if (packages != null) {
+ serializer.startTag(null, TAG_BLOCK_UNINSTALL_PACKAGES);
+ for (int i = 0; i < packages.size(); i++) {
+ serializer.startTag(null, TAG_BLOCK_UNINSTALL);
+ serializer.attribute(null, ATTR_PACKAGE_NAME, packages.valueAt(i));
+ serializer.endTag(null, TAG_BLOCK_UNINSTALL);
+ }
+ serializer.endTag(null, TAG_BLOCK_UNINSTALL_PACKAGES);
+ }
+ }
+
void writePackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -2038,9 +2114,6 @@
if (ustate.suspended) {
serializer.attribute(null, ATTR_SUSPENDED, "true");
}
- if (ustate.blockUninstall) {
- serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
- }
if (ustate.instantApp) {
serializer.attribute(null, ATTR_INSTANT_APP, "true");
}
@@ -2091,6 +2164,7 @@
writePersistentPreferredActivitiesLPr(serializer, userId);
writeCrossProfileIntentFiltersLPr(serializer, userId);
writeDefaultAppsLPr(serializer, userId);
+ writeBlockUninstallPackagesLPr(serializer, userId);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 4a8232d..be50eee 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -62,6 +62,7 @@
// Maps package name to code locations.
// It caches the code locations for the installed packages. This allows for
// faster lookups (no locks) when finding what package owns the dex file.
+ @GuardedBy("mPackageCodeLocationsCache")
private final Map<String, PackageCodeLocations> mPackageCodeLocationsCache;
// PackageDexUsage handles the actual I/O operations. It is responsible to
@@ -206,7 +207,7 @@
// In case there was an update, write the package use info to disk async.
// Note that we do the writing here and not in PackageDexUsage in order to be
// consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
- // multiple updates in PackaeDexUsage before writing it).
+ // multiple updates in PackageDexUsage before writing it).
if (mPackageDexUsage.clearUsedByOtherApps(packageName)) {
mPackageDexUsage.maybeWriteAsync();
}
@@ -226,7 +227,7 @@
// In case there was an update, write the package use info to disk async.
// Note that we do the writing here and not in PackageDexUsage in order to be
// consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
- // multiple updates in PackaeDexUsage before writing it).
+ // multiple updates in PackageDexUsage before writing it).
if (updated) {
mPackageDexUsage.maybeWriteAsync();
}
@@ -245,17 +246,22 @@
private void cachePackageCodeLocation(String packageName, String baseCodePath,
String[] splitCodePaths, String[] dataDirs, int userId) {
- PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName,
- new PackageCodeLocations(packageName, baseCodePath, splitCodePaths));
- pcl.updateCodeLocation(baseCodePath, splitCodePaths);
- if (dataDirs != null) {
- for (String dataDir : dataDirs) {
- // The set of data dirs includes deviceProtectedDataDir and
- // credentialProtectedDataDir which might be null for shared
- // libraries. Currently we don't track these but be lenient
- // and check in case we ever decide to store their usage data.
- if (dataDir != null) {
- pcl.mergeAppDataDirs(dataDir, userId);
+ synchronized (mPackageCodeLocationsCache) {
+ PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName,
+ new PackageCodeLocations(packageName, baseCodePath, splitCodePaths));
+ // TODO(calin): We are forced to extend the scope of this synchronization because
+ // the values of the cache (PackageCodeLocations) are updated in place.
+ // Make PackageCodeLocations immutable to simplify the synchronization reasoning.
+ pcl.updateCodeLocation(baseCodePath, splitCodePaths);
+ if (dataDirs != null) {
+ for (String dataDir : dataDirs) {
+ // The set of data dirs includes deviceProtectedDataDir and
+ // credentialProtectedDataDir which might be null for shared
+ // libraries. Currently we don't track these but be lenient
+ // and check in case we ever decide to store their usage data.
+ if (dataDir != null) {
+ pcl.mergeAppDataDirs(dataDir, userId);
+ }
}
}
}
@@ -527,10 +533,12 @@
// The loadingPackage does not own the dex file.
// Perform a reverse look-up in the cache to detect if any package has ownership.
// Note that we can have false negatives if the cache falls out of date.
- for (PackageCodeLocations pcl : mPackageCodeLocationsCache.values()) {
- outcome = pcl.searchDex(dexPath, userId);
- if (outcome != DEX_SEARCH_NOT_FOUND) {
- return new DexSearchResult(pcl.mPackageName, outcome);
+ synchronized (mPackageCodeLocationsCache) {
+ for (PackageCodeLocations pcl : mPackageCodeLocationsCache.values()) {
+ outcome = pcl.searchDex(dexPath, userId);
+ if (outcome != DEX_SEARCH_NOT_FOUND) {
+ return new DexSearchResult(pcl.mPackageName, outcome);
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 91a163a..6120ff4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -184,6 +184,7 @@
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.service.vr.IPersistentVrStateCallbacks;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
import android.util.DisplayMetrics;
@@ -512,6 +513,8 @@
volatile boolean mGoingToSleep;
volatile boolean mRecentsVisible;
volatile boolean mPictureInPictureVisible;
+ // Written by vr manager thread, only read in this class.
+ volatile private boolean mPersistentVrModeEnabled;
volatile private boolean mDismissImeOnBackKeyPressed;
// Used to hold the last user key used to wake the device. This helps us prevent up events
@@ -581,6 +584,8 @@
boolean mTranslucentDecorEnabled = true;
boolean mUseTvRouting;
+ private boolean mHandleVolumeKeysInWM;
+
int mPointerLocationMode = 0; // guarded by mLock
// The last window we were told about in focusChanged.
@@ -777,6 +782,10 @@
private boolean mBugreportTvKey2Pressed;
private boolean mBugreportTvScheduled;
+ private boolean mAccessibilityTvKey1Pressed;
+ private boolean mAccessibilityTvKey2Pressed;
+ private boolean mAccessibilityTvScheduled;
+
/* The number of steps between min and max brightness */
private static final int BRIGHTNESS_STEPS = 10;
@@ -821,6 +830,7 @@
private static final int MSG_BACK_DELAYED_PRESS = 20;
private static final int MSG_ACCESSIBILITY_SHORTCUT = 21;
private static final int MSG_BUGREPORT_TV = 22;
+ private static final int MSG_ACCESSIBILITY_TV = 23;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -902,6 +912,11 @@
case MSG_BUGREPORT_TV:
takeBugreport();
break;
+ case MSG_ACCESSIBILITY_TV:
+ if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) {
+ accessibilityShortcutActivated();
+ }
+ break;
}
}
}
@@ -1002,6 +1017,14 @@
}
MyOrientationListener mOrientationListener;
+ final IPersistentVrStateCallbacks mPersistentVrModeListener =
+ new IPersistentVrStateCallbacks.Stub() {
+ @Override
+ public void onPersistentVrStateChanged(boolean enabled) {
+ mPersistentVrModeEnabled = enabled;
+ }
+ };
+
private final StatusBarController mStatusBarController = new StatusBarController();
private final BarController mNavigationBarController = new BarController("NavigationBar",
@@ -1917,6 +1940,9 @@
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
+ mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_handleVolumeKeysInWindowManager);
+
readConfigurationDependentBehaviors();
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
@@ -3308,6 +3334,11 @@
if (!down) {
cancelPreloadRecentApps();
+ if (mHasFeatureLeanback) {
+ // Clear flags
+ mAccessibilityTvKey2Pressed = down;
+ }
+
mHomePressed = false;
if (mHomeConsumed) {
mHomeConsumed = false;
@@ -3362,6 +3393,13 @@
preloadRecentApps();
}
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+ if (mHasFeatureLeanback) {
+ mAccessibilityTvKey2Pressed = down;
+ if (interceptAccessibilityGestureTv()) {
+ return -1;
+ }
+ }
+
if (!keyguardOn) {
handleLongPressOnHome(event.getDeviceId());
}
@@ -3511,16 +3549,28 @@
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
- if (mUseTvRouting) {
- // On TVs volume keys never go to the foreground app.
+ if (mUseTvRouting || mHandleVolumeKeysInWM) {
+ // On TVs or when the configuration is enabled, volume keys never
+ // go to the foreground app.
dispatchDirectAudioEvent(event);
return -1;
}
+
+ // If the device is in Vr mode, drop the volume keys and don't
+ // forward it to the application/dispatch the audio event.
+ if (mPersistentVrModeEnabled) {
+ return -1;
+ }
} else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) {
// Pass through keyboard navigation keys.
return 0;
} else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) {
return -1;
+ } else if (mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ mAccessibilityTvKey1Pressed = down;
+ if (interceptAccessibilityGestureTv()) {
+ return -1;
+ }
}
// Toggle Caps Lock on META-ALT.
@@ -3739,6 +3789,25 @@
return mBugreportTvScheduled;
}
+ /**
+ * TV only: recognizes a remote control gesture as Accessibility shortcut.
+ * Shortcut: Long press (HOME + DPAD_CENTER)
+ */
+ private boolean interceptAccessibilityGestureTv() {
+ if (mAccessibilityTvKey1Pressed && mAccessibilityTvKey2Pressed) {
+ if (!mAccessibilityTvScheduled) {
+ mAccessibilityTvScheduled = true;
+ Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+ } else if (mAccessibilityTvScheduled) {
+ mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
+ mAccessibilityTvScheduled = false;
+ }
+ return mAccessibilityTvScheduled;
+ }
+
private void takeBugreport() {
if ("1".equals(SystemProperties.get("ro.debuggable"))
|| Settings.Global.getInt(mContext.getContentResolver(),
@@ -5900,8 +5969,8 @@
}
}
}
- if (mUseTvRouting) {
- // On TVs, defer special key handlings to
+ if (mUseTvRouting || mHandleVolumeKeysInWM) {
+ // Defer special key handlings to
// {@link interceptKeyBeforeDispatching()}.
result |= ACTION_PASS_TO_USER;
} else if ((result & ACTION_PASS_TO_USER) == 0) {
@@ -6909,7 +6978,13 @@
|| mAllowAllRotations == 1
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
- preferredRotation = sensorRotation;
+ // In VrMode, we report the sensor as always being in default orientation so:
+ // 1) The orientation doesn't change as the user moves their head.
+ // 2) 2D apps within VR show in the device's default orientation.
+ // This only overwrites the sensor-provided orientation and does not affect any
+ // explicit orientation preferences specified by any activities.
+ preferredRotation =
+ mPersistentVrModeEnabled ? Surface.ROTATION_0 : sensorRotation;
} else {
preferredRotation = lastRotation;
}
@@ -7083,6 +7158,9 @@
mKeyguardDelegate.onSystemReady();
mVrManagerInternal = LocalServices.getService(VrManagerInternal.class);
+ if (mVrManagerInternal != null) {
+ mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
+ }
readCameraLensCoverState();
updateUiMode();
@@ -8215,6 +8293,14 @@
pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation);
pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
+ if (mHasFeatureLeanback) {
+ pw.print(prefix);
+ pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed);
+ pw.print(prefix);
+ pw.print("mAccessibilityTvKey2Pressed="); pw.println(mAccessibilityTvKey2Pressed);
+ pw.print(prefix);
+ pw.print("mAccessibilityTvScheduled="); pw.println(mAccessibilityTvScheduled);
+ }
mGlobalKeyManager.dump(prefix, pw);
mStatusBarController.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/radio/Tuner.java b/services/core/java/com/android/server/radio/Tuner.java
index a06d8c6..0f40883 100644
--- a/services/core/java/com/android/server/radio/Tuner.java
+++ b/services/core/java/com/android/server/radio/Tuner.java
@@ -32,10 +32,13 @@
private final long mNativeContext;
private final Object mLock = new Object();
+ private boolean mIsMuted = false;
private int mRegion; // TODO(b/36863239): find better solution to manage regions
+ private final boolean mWithAudio;
- Tuner(@NonNull ITunerCallback clientCallback, int region) {
+ Tuner(@NonNull ITunerCallback clientCallback, int region, boolean withAudio) {
mRegion = region;
+ mWithAudio = withAudio;
mNativeContext = nativeInit(clientCallback);
}
@@ -86,4 +89,29 @@
Slog.d(TAG, "getProgramInformation()");
return RadioManager.STATUS_INVALID_OPERATION;
}
+
+ @Override
+ public void setMuted(boolean mute) {
+ if (!mWithAudio) {
+ throw new IllegalStateException("Can't operate on mute - no audio requested");
+ }
+ synchronized (mLock) {
+ if (mIsMuted == mute) return;
+ mIsMuted = mute;
+
+ // TODO(b/34348946): notifify audio policy manager of media activity on radio audio
+ // device. This task is pulled directly from previous implementation of native service.
+ }
+ }
+
+ @Override
+ public boolean isMuted() {
+ if (!mWithAudio) {
+ Slog.w(TAG, "Tuner did not request audio, pretending it was muted");
+ return true;
+ }
+ synchronized (mLock) {
+ return mIsMuted;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/timezone/CheckToken.java b/services/core/java/com/android/server/timezone/CheckToken.java
new file mode 100644
index 0000000..5128360
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/CheckToken.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * A deserialized version of the byte[] sent to the time zone update application to identify a
+ * triggered time zone update check. It encodes the optimistic lock ID used to detect
+ * concurrent checks and the minimal package versions that will have been checked.
+ */
+final class CheckToken {
+
+ final int mOptimisticLockId;
+ final PackageVersions mPackageVersions;
+
+ CheckToken(int optimisticLockId, PackageVersions packageVersions) {
+ this.mOptimisticLockId = optimisticLockId;
+
+ if (packageVersions == null) {
+ throw new NullPointerException("packageVersions == null");
+ }
+ this.mPackageVersions = packageVersions;
+ }
+
+ byte[] toByteArray() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(12 /* (3 * sizeof(int)) */);
+ try (DataOutputStream dos = new DataOutputStream(baos)) {
+ dos.writeInt(mOptimisticLockId);
+ dos.writeInt(mPackageVersions.mUpdateAppVersion);
+ dos.writeInt(mPackageVersions.mDataAppVersion);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to write into a ByteArrayOutputStream", e);
+ }
+ return baos.toByteArray();
+ }
+
+ static CheckToken fromByteArray(byte[] tokenBytes) throws IOException {
+ ByteArrayInputStream bais = new ByteArrayInputStream(tokenBytes);
+ try (DataInputStream dis = new DataInputStream(bais)) {
+ int versionId = dis.readInt();
+ int updateAppVersion = dis.readInt();
+ int dataAppVersion = dis.readInt();
+ return new CheckToken(versionId, new PackageVersions(updateAppVersion, dataAppVersion));
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CheckToken checkToken = (CheckToken) o;
+
+ if (mOptimisticLockId != checkToken.mOptimisticLockId) {
+ return false;
+ }
+ return mPackageVersions.equals(checkToken.mPackageVersions);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mOptimisticLockId;
+ result = 31 * result + mPackageVersions.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Token{" +
+ "mOptimisticLockId=" + mOptimisticLockId +
+ ", mPackageVersions=" + mPackageVersions +
+ '}';
+ }
+}
diff --git a/core/java/android/app/CompatibilityDisplayProperties.aidl b/services/core/java/com/android/server/timezone/ClockHelper.java
similarity index 75%
copy from core/java/android/app/CompatibilityDisplayProperties.aidl
copy to services/core/java/com/android/server/timezone/ClockHelper.java
index 626a63e..353728a 100644
--- a/core/java/android/app/CompatibilityDisplayProperties.aidl
+++ b/services/core/java/com/android/server/timezone/ClockHelper.java
@@ -14,7 +14,12 @@
* limitations under the License.
*/
-package android.app;
+package com.android.server.timezone;
-/** @hide */
-parcelable CompatibilityDisplayProperties;
+/**
+ * An easy-to-mock interface for obtaining a monotonically increasing time value in milliseconds.
+ */
+interface ClockHelper {
+
+ long currentTimestamp();
+}
diff --git a/services/core/java/com/android/server/timezone/ConfigHelper.java b/services/core/java/com/android/server/timezone/ConfigHelper.java
new file mode 100644
index 0000000..f9984fa
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/ConfigHelper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+/**
+ * An easy-to-mock interface around device config for use by {@link PackageTracker}; it is not
+ * possible to test various states with the real one because config is fixed in the system image.
+ */
+interface ConfigHelper {
+
+ boolean isTrackingEnabled();
+
+ String getUpdateAppPackageName();
+
+ String getDataAppPackageName();
+
+ int getCheckTimeAllowedMillis();
+
+ int getFailedCheckRetryCount();
+}
diff --git a/core/java/android/app/CompatibilityDisplayProperties.aidl b/services/core/java/com/android/server/timezone/FileDescriptorHelper.java
similarity index 63%
copy from core/java/android/app/CompatibilityDisplayProperties.aidl
copy to services/core/java/com/android/server/timezone/FileDescriptorHelper.java
index 626a63e..c3b1101 100644
--- a/core/java/android/app/CompatibilityDisplayProperties.aidl
+++ b/services/core/java/com/android/server/timezone/FileDescriptorHelper.java
@@ -14,7 +14,17 @@
* limitations under the License.
*/
-package android.app;
+package com.android.server.timezone;
-/** @hide */
-parcelable CompatibilityDisplayProperties;
+import android.os.ParcelFileDescriptor;
+
+import java.io.IOException;
+
+/**
+ * An easy-to-mock interface around use of {@link ParcelFileDescriptor} for use by
+ * {@link RulesManagerService}.
+ */
+interface FileDescriptorHelper {
+
+ byte[] readFully(ParcelFileDescriptor parcelFileDescriptor) throws IOException;
+}
diff --git a/services/core/java/com/android/server/timezone/IntentHelper.java b/services/core/java/com/android/server/timezone/IntentHelper.java
new file mode 100644
index 0000000..0cb9065
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/IntentHelper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+/**
+ * An easy-to-mock interface around intent sending / receiving for use by {@link PackageTracker};
+ * it is not possible to test various cases with the real one because of the need to simulate
+ * receiving and broadcasting intents.
+ */
+interface IntentHelper {
+
+ void initialize(String updateAppPackageName, String dataAppPackageName, Listener listener);
+
+ void sendTriggerUpdateCheck(CheckToken checkToken);
+
+ void enableReliabilityTriggering();
+
+ void disableReliabilityTriggering();
+
+ interface Listener {
+ void triggerUpdateIfNeeded(boolean packageUpdated);
+ }
+}
diff --git a/services/core/java/com/android/server/timezone/IntentHelperImpl.java b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
new file mode 100644
index 0000000..3ffbb2d
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import android.app.timezone.RulesUpdaterContract;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PatternMatcher;
+import android.util.Slog;
+
+import java.util.regex.Pattern;
+
+/**
+ * The bona fide implementation of {@link IntentHelper}.
+ */
+final class IntentHelperImpl implements IntentHelper {
+
+ private final static String TAG = "timezone.IntentHelperImpl";
+
+ private final Context mContext;
+ private String mUpdaterAppPackageName;
+
+ private boolean mReliabilityReceiverEnabled;
+ private Receiver mReliabilityReceiver;
+
+ IntentHelperImpl(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void initialize(
+ String updaterAppPackageName, String dataAppPackageName, Listener listener) {
+ mUpdaterAppPackageName = updaterAppPackageName;
+
+ // Register for events of interest.
+
+ // The intent filter that triggers when package update events happen that indicate there may
+ // be work to do.
+ IntentFilter packageIntentFilter = new IntentFilter();
+ // Either of these mean a downgrade?
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ packageIntentFilter.addDataScheme("package");
+ packageIntentFilter.addDataSchemeSpecificPart(
+ updaterAppPackageName, PatternMatcher.PATTERN_LITERAL);
+ packageIntentFilter.addDataSchemeSpecificPart(
+ dataAppPackageName, PatternMatcher.PATTERN_LITERAL);
+ Receiver packageUpdateReceiver = new Receiver(listener, true /* packageUpdated */);
+ mContext.registerReceiver(packageUpdateReceiver, packageIntentFilter);
+
+ // TODO(nfuller): Add more exotic intents as needed. e.g.
+ // packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ // Also, disabled...?
+ mReliabilityReceiver = new Receiver(listener, false /* packageUpdated */);
+ }
+
+ /** Sends an intent to trigger an update check. */
+ @Override
+ public void sendTriggerUpdateCheck(CheckToken checkToken) {
+ RulesUpdaterContract.sendBroadcast(
+ mContext, mUpdaterAppPackageName, checkToken.toByteArray());
+ }
+
+ @Override
+ public synchronized void enableReliabilityTriggering() {
+ if (!mReliabilityReceiverEnabled) {
+ // The intent filter that exists to make updates reliable in the event of failures /
+ // reboots.
+ IntentFilter reliabilityIntentFilter = new IntentFilter();
+ reliabilityIntentFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
+ mContext.registerReceiver(mReliabilityReceiver, reliabilityIntentFilter);
+ mReliabilityReceiverEnabled = true;
+ }
+ }
+
+ @Override
+ public synchronized void disableReliabilityTriggering() {
+ if (mReliabilityReceiverEnabled) {
+ mContext.unregisterReceiver(mReliabilityReceiver);
+ mReliabilityReceiverEnabled = false;
+ }
+ }
+
+ private static class Receiver extends BroadcastReceiver {
+ private final Listener mListener;
+ private final boolean mPackageUpdated;
+
+ private Receiver(Listener listener, boolean packageUpdated) {
+ mListener = listener;
+ mPackageUpdated = packageUpdated;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.d(TAG, "Received intent: " + intent.toString());
+ mListener.triggerUpdateIfNeeded(mPackageUpdated);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/timezone/PackageManagerHelper.java b/services/core/java/com/android/server/timezone/PackageManagerHelper.java
new file mode 100644
index 0000000..804941a
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/PackageManagerHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+
+/**
+ * An easy-to-mock facade around PackageManager for use by {@link PackageTracker}; it is not
+ * possible to test various cases with the real one because of the need to simulate package versions
+ * and manifest configurations.
+ */
+interface PackageManagerHelper {
+
+ int getInstalledPackageVersion(String packageName)
+ throws PackageManager.NameNotFoundException;
+
+ boolean isPrivilegedApp(String packageName) throws PackageManager.NameNotFoundException;
+
+ boolean usesPermission(String packageName, String requiredPermissionName)
+ throws PackageManager.NameNotFoundException;
+
+ boolean contentProviderRegistered(String authority, String requiredPackageName);
+
+ boolean receiverRegistered(Intent intent, String requiredPermissionName)
+ throws PackageManager.NameNotFoundException;
+}
diff --git a/services/core/java/com/android/server/timezone/PackageStatus.java b/services/core/java/com/android/server/timezone/PackageStatus.java
new file mode 100644
index 0000000..63790961
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/PackageStatus.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Information about the status of the time zone update / data packages that are persisted by the
+ * Android system.
+ */
+final class PackageStatus {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ CHECK_STARTED, CHECK_COMPLETED_SUCCESS, CHECK_COMPLETED_FAILURE })
+ @interface CheckStatus {}
+
+ /** A time zone update check has been started but not yet completed. */
+ static final int CHECK_STARTED = 1;
+ /** A time zone update check has been completed and succeeded. */
+ static final int CHECK_COMPLETED_SUCCESS = 2;
+ /** A time zone update check has been completed and failed. */
+ static final int CHECK_COMPLETED_FAILURE = 3;
+
+ @CheckStatus
+ final int mCheckStatus;
+
+ // Non-null
+ final PackageVersions mVersions;
+
+ PackageStatus(@CheckStatus int checkStatus, PackageVersions versions) {
+ this.mCheckStatus = checkStatus;
+ if (checkStatus < 1 || checkStatus > 3) {
+ throw new IllegalArgumentException("Unknown checkStatus " + checkStatus);
+ }
+ if (versions == null) {
+ throw new NullPointerException("versions == null");
+ }
+ this.mVersions = versions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ PackageStatus that = (PackageStatus) o;
+
+ if (mCheckStatus != that.mCheckStatus) {
+ return false;
+ }
+ return mVersions.equals(that.mVersions);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mCheckStatus;
+ result = 31 * result + mVersions.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "PackageStatus{" +
+ "mCheckStatus=" + mCheckStatus +
+ ", mVersions=" + mVersions +
+ '}';
+ }
+}
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
new file mode 100644
index 0000000..31f0e31
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Slog;
+
+import java.io.File;
+
+import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_FAILURE;
+import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_SUCCESS;
+import static com.android.server.timezone.PackageStatus.CHECK_STARTED;
+
+/**
+ * Storage logic for accessing/mutating the Android system's persistent state related to time zone
+ * update checking. There is expected to be a single instance and all methods synchronized on
+ * {@code this} for thread safety.
+ */
+final class PackageStatusStorage {
+
+ private static final String TAG = "timezone.PackageStatusStorage";
+
+ private static final String DATABASE_NAME = "timezonepackagestatus.db";
+ private static final int DATABASE_VERSION = 1;
+
+ /** The table name. It will have a single row with _id == {@link #SINGLETON_ID} */
+ private static final String TABLE = "status";
+ private static final String COLUMN_ID = "_id";
+
+ /**
+ * Column that stores a monotonically increasing lock ID, used to detect concurrent update
+ * issues without on-line locks. Incremented on every write.
+ */
+ private static final String COLUMN_OPTIMISTIC_LOCK_ID = "optimistic_lock_id";
+
+ /**
+ * Column that stores the current "check status" of the time zone update application packages.
+ */
+ private static final String COLUMN_CHECK_STATUS = "check_status";
+
+ /**
+ * Column that stores the version of the time zone rules update application being checked / last
+ * checked.
+ */
+ private static final String COLUMN_UPDATE_APP_VERSION = "update_app_package_version";
+
+ /**
+ * Column that stores the version of the time zone rules data application being checked / last
+ * checked.
+ */
+ private static final String COLUMN_DATA_APP_VERSION = "data_app_package_version";
+
+ /**
+ * The ID of the one row.
+ */
+ private static final int SINGLETON_ID = 1;
+
+ private static final int UNKNOWN_PACKAGE_VERSION = -1;
+
+ private final DatabaseHelper mDatabaseHelper;
+
+ PackageStatusStorage(Context context) {
+ mDatabaseHelper = new DatabaseHelper(context);
+ }
+
+ void deleteDatabaseForTests() {
+ SQLiteDatabase.deleteDatabase(mDatabaseHelper.getDatabaseFile());
+ }
+
+ /**
+ * Obtain the current check status of the application packages. Returns {@code null} the first
+ * time it is called, or after {@link #resetCheckState()}.
+ */
+ PackageStatus getPackageStatus() {
+ synchronized (this) {
+ try {
+ return getPackageStatusInternal();
+ } catch (IllegalArgumentException e) {
+ // This means that data exists in the table but it was bad.
+ Slog.e(TAG, "Package status invalid, resetting and retrying", e);
+
+ // Reset the storage so it is in a good state again.
+ mDatabaseHelper.recoverFromBadData();
+ return getPackageStatusInternal();
+ }
+ }
+ }
+
+ private PackageStatus getPackageStatusInternal() {
+ String[] columns = {
+ COLUMN_CHECK_STATUS, COLUMN_UPDATE_APP_VERSION, COLUMN_DATA_APP_VERSION
+ };
+ Cursor cursor = mDatabaseHelper.getReadableDatabase()
+ .query(TABLE, columns, COLUMN_ID + " = ?",
+ new String[] { Integer.toString(SINGLETON_ID) },
+ null /* groupBy */, null /* having */, null /* orderBy */);
+ if (cursor.getCount() != 1) {
+ Slog.e(TAG, "Unable to find package status from package status row. Rows returned: "
+ + cursor.getCount());
+ return null;
+ }
+ cursor.moveToFirst();
+
+ // Determine check status.
+ if (cursor.isNull(0)) {
+ // This is normal the first time getPackageStatus() is called, or after
+ // resetCheckState().
+ return null;
+ }
+ int checkStatus = cursor.getInt(0);
+
+ // Determine package version.
+ if (cursor.isNull(1) || cursor.isNull(2)) {
+ Slog.e(TAG, "Package version information unexpectedly null");
+ return null;
+ }
+ PackageVersions packageVersions = new PackageVersions(cursor.getInt(1), cursor.getInt(2));
+
+ return new PackageStatus(checkStatus, packageVersions);
+ }
+
+ /**
+ * Generate a new {@link CheckToken} that can be passed to the time zone rules update
+ * application.
+ */
+ CheckToken generateCheckToken(PackageVersions currentInstalledVersions) {
+ if (currentInstalledVersions == null) {
+ throw new NullPointerException("currentInstalledVersions == null");
+ }
+
+ synchronized (this) {
+ Integer optimisticLockId = getCurrentOptimisticLockId();
+ if (optimisticLockId == null) {
+ Slog.w(TAG, "Unable to find optimistic lock ID from package status row");
+
+ // Recover.
+ optimisticLockId = mDatabaseHelper.recoverFromBadData();
+ }
+
+ int newOptimisticLockId = optimisticLockId + 1;
+ boolean statusRowUpdated = writeStatusRow(
+ optimisticLockId, newOptimisticLockId, CHECK_STARTED, currentInstalledVersions);
+ if (!statusRowUpdated) {
+ Slog.e(TAG, "Unable to update status to CHECK_STARTED in package status row."
+ + " synchronization failure?");
+ return null;
+ }
+ return new CheckToken(newOptimisticLockId, currentInstalledVersions);
+ }
+ }
+
+ /**
+ * Reset the current device state to "unknown".
+ */
+ void resetCheckState() {
+ synchronized(this) {
+ Integer optimisticLockId = getCurrentOptimisticLockId();
+ if (optimisticLockId == null) {
+ Slog.w(TAG, "resetCheckState: Unable to find optimistic lock ID from package"
+ + " status row");
+ // Attempt to recover the storage state.
+ optimisticLockId = mDatabaseHelper.recoverFromBadData();
+ }
+
+ int newOptimisticLockId = optimisticLockId + 1;
+ if (!writeStatusRow(optimisticLockId, newOptimisticLockId,
+ null /* status */, null /* packageVersions */)) {
+ Slog.e(TAG, "resetCheckState: Unable to reset package status row,"
+ + " newOptimisticLockId=" + newOptimisticLockId);
+ }
+ }
+ }
+
+ /**
+ * Update the current device state if possible. Returns true if the update was successful.
+ * {@code false} indicates the storage has been changed since the {@link CheckToken} was
+ * generated and the update was discarded.
+ */
+ boolean markChecked(CheckToken checkToken, boolean succeeded) {
+ synchronized (this) {
+ int optimisticLockId = checkToken.mOptimisticLockId;
+ int newOptimisticLockId = optimisticLockId + 1;
+ int status = succeeded ? CHECK_COMPLETED_SUCCESS : CHECK_COMPLETED_FAILURE;
+ return writeStatusRow(optimisticLockId, newOptimisticLockId,
+ status, checkToken.mPackageVersions);
+ }
+ }
+
+ // Caller should be synchronized(this)
+ private Integer getCurrentOptimisticLockId() {
+ final String[] columns = { COLUMN_OPTIMISTIC_LOCK_ID };
+ final String querySelection = COLUMN_ID + " = ?";
+ final String[] querySelectionArgs = { Integer.toString(SINGLETON_ID) };
+
+ SQLiteDatabase database = mDatabaseHelper.getReadableDatabase();
+ try (Cursor cursor = database.query(TABLE, columns, querySelection, querySelectionArgs,
+ null /* groupBy */, null /* having */, null /* orderBy */)) {
+ if (cursor.getCount() != 1) {
+ Slog.w(TAG, cursor.getCount() + " rows returned, expected exactly one.");
+ return null;
+ }
+ cursor.moveToFirst();
+ return cursor.getInt(0);
+ }
+ }
+
+ // Caller should be synchronized(this)
+ private boolean writeStatusRow(int optimisticLockId, int newOptimisticLockId, Integer status,
+ PackageVersions packageVersions) {
+ if ((status == null) != (packageVersions == null)) {
+ throw new IllegalArgumentException(
+ "Provide both status and packageVersions, or neither.");
+ }
+
+ SQLiteDatabase database = mDatabaseHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_OPTIMISTIC_LOCK_ID, newOptimisticLockId);
+ if (status == null) {
+ values.putNull(COLUMN_CHECK_STATUS);
+ values.put(COLUMN_UPDATE_APP_VERSION, UNKNOWN_PACKAGE_VERSION);
+ values.put(COLUMN_DATA_APP_VERSION, UNKNOWN_PACKAGE_VERSION);
+ } else {
+ values.put(COLUMN_CHECK_STATUS, status);
+ values.put(COLUMN_UPDATE_APP_VERSION, packageVersions.mUpdateAppVersion);
+ values.put(COLUMN_DATA_APP_VERSION, packageVersions.mDataAppVersion);
+ }
+
+ String updateSelection = COLUMN_ID + " = ? AND " + COLUMN_OPTIMISTIC_LOCK_ID + " = ?";
+ String[] updateSelectionArgs = {
+ Integer.toString(SINGLETON_ID), Integer.toString(optimisticLockId)
+ };
+ int count = database.update(TABLE, values, updateSelection, updateSelectionArgs);
+ if (count > 1) {
+ // This has to be because of corruption: there should only ever be one row.
+ Slog.w(TAG, "writeStatusRow: " + count + " rows updated, expected exactly one.");
+ // Reset the table.
+ mDatabaseHelper.recoverFromBadData();
+ }
+
+ // 1 is the success case. 0 rows updated means the row is missing or the optimistic lock ID
+ // was not as expected, this could be because of corruption but is most likely due to an
+ // optimistic lock failure. Callers can decide on a case-by-case basis.
+ return count == 1;
+ }
+
+ /** Only used during tests to force an empty table. */
+ void deleteRowForTests() {
+ mDatabaseHelper.getWritableDatabase().delete(TABLE, null, null);
+ }
+
+ /** Only used during tests to force a known table state. */
+ public void forceCheckStateForTests(int checkStatus, PackageVersions packageVersions) {
+ int optimisticLockId = getCurrentOptimisticLockId();
+ writeStatusRow(optimisticLockId, optimisticLockId, checkStatus, packageVersions);
+ }
+
+ static class DatabaseHelper extends SQLiteOpenHelper {
+
+ private final Context mContext;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ mContext = context;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE + " (" +
+ "_id INTEGER PRIMARY KEY," +
+ COLUMN_OPTIMISTIC_LOCK_ID + " INTEGER NOT NULL," +
+ COLUMN_CHECK_STATUS + " INTEGER," +
+ COLUMN_UPDATE_APP_VERSION + " INTEGER NOT NULL," +
+ COLUMN_DATA_APP_VERSION + " INTEGER NOT NULL" +
+ ");");
+ insertInitialRowState(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+ // no-op: nothing to upgrade
+ }
+
+ /** Recover the initial data row state, returning the new current optimistic lock ID */
+ int recoverFromBadData() {
+ // Delete the table content.
+ SQLiteDatabase writableDatabase = getWritableDatabase();
+ writableDatabase.delete(TABLE, null /* whereClause */, null /* whereArgs */);
+
+ // Insert the initial content.
+ return insertInitialRowState(writableDatabase);
+ }
+
+ /** Insert the initial data row, returning the optimistic lock ID */
+ private static int insertInitialRowState(SQLiteDatabase db) {
+ // Doesn't matter what it is, but we avoid the obvious starting value each time the row
+ // is reset to ensure that old tokens are unlikely to work.
+ final int initialOptimisticLockId = (int) System.currentTimeMillis();
+
+ // Insert the one row.
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_ID, SINGLETON_ID);
+ values.put(COLUMN_OPTIMISTIC_LOCK_ID, initialOptimisticLockId);
+ values.putNull(COLUMN_CHECK_STATUS);
+ values.put(COLUMN_UPDATE_APP_VERSION, UNKNOWN_PACKAGE_VERSION);
+ values.put(COLUMN_DATA_APP_VERSION, UNKNOWN_PACKAGE_VERSION);
+ long id = db.insert(TABLE, null, values);
+ if (id == -1) {
+ Slog.w(TAG, "insertInitialRow: could not insert initial row, id=" + id);
+ return -1;
+ }
+ return initialOptimisticLockId;
+ }
+
+ File getDatabaseFile() {
+ return mContext.getDatabasePath(DATABASE_NAME);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
new file mode 100644
index 0000000..8abf7df
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import android.app.timezone.RulesUpdaterContract;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.TimeZoneRulesDataContract;
+import android.util.Slog;
+
+/**
+ * Monitors the installed applications associated with time zone updates. If the app packages are
+ * updated it indicates there <em>might</em> be a time zone rules update to apply so a targeted
+ * broadcast intent is used to trigger the time zone updater app.
+ *
+ * <p>The "update triggering" behavior of this component can be disabled via device configuration.
+ *
+ * <p>The package tracker listens for package updates of the time zone "updater app" and "data app".
+ * It also listens for "reliability" triggers. Reliability triggers are there to ensure that the
+ * package tracker handles failures reliably and are "idle maintenance" events or something similar.
+ * Reliability triggers can cause a time zone update check to take place if the current state is
+ * unclear. For example, it can be unclear after boot or after a failure. If there are repeated
+ * failures reliability updates are halted until the next boot.
+ *
+ * <p>This component keeps persistent track of the most recent app packages checked to avoid
+ * unnecessary expense from broadcasting intents (which will cause other app processes to spawn).
+ * The current status is also stored to detect whether the most recently-generated check is
+ * complete successfully. For example, if the device was interrupted while doing a check and never
+ * acknowledged a check then a check will be retried the next time a "reliability trigger" event
+ * happens.
+ */
+// Also made non-final so it can be mocked.
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class PackageTracker implements IntentHelper.Listener {
+ private static final String TAG = "timezone.PackageTracker";
+
+ private final PackageManagerHelper mPackageManagerHelper;
+ private final IntentHelper mIntentHelper;
+ private final ConfigHelper mConfigHelper;
+ private final PackageStatusStorage mPackageStatusStorage;
+ private final ClockHelper mClockHelper;
+
+ // False if tracking is disabled.
+ private boolean mTrackingEnabled;
+
+ // These fields may be null if package tracking is disabled.
+ private String mUpdateAppPackageName;
+ private String mDataAppPackageName;
+
+ // The time a triggered check is allowed to take before it is considered overdue.
+ private int mCheckTimeAllowedMillis;
+ // The number of failed checks in a row before reliability checks should stop happening.
+ private long mFailedCheckRetryCount;
+
+ // Reliability check state: If a check was triggered but not acknowledged within
+ // mCheckTimeAllowedMillis then another one can be triggered.
+ private Long mLastTriggerTimestamp = null;
+
+ // Reliability check state: Whether any checks have been triggered at all.
+ private boolean mCheckTriggered;
+
+ // Reliability check state: A count of how many failures have occurred consecutively.
+ private int mCheckFailureCount;
+
+ /** Creates the {@link PackageTracker} for normal use. */
+ static PackageTracker create(Context context) {
+ PackageTrackerHelperImpl helperImpl = new PackageTrackerHelperImpl(context);
+ return new PackageTracker(
+ helperImpl /* clock */,
+ helperImpl /* configHelper */,
+ helperImpl /* packageManagerHelper */,
+ new PackageStatusStorage(context),
+ new IntentHelperImpl(context));
+ }
+
+ // A constructor that can be used by tests to supply mocked / faked dependencies.
+ PackageTracker(ClockHelper clockHelper, ConfigHelper configHelper,
+ PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage,
+ IntentHelper intentHelper) {
+ mClockHelper = clockHelper;
+ mConfigHelper = configHelper;
+ mPackageManagerHelper = packageManagerHelper;
+ mPackageStatusStorage = packageStatusStorage;
+ mIntentHelper = intentHelper;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected synchronized void start() {
+ mTrackingEnabled = mConfigHelper.isTrackingEnabled();
+ if (!mTrackingEnabled) {
+ Slog.i(TAG, "Time zone updater / data package tracking explicitly disabled.");
+ return;
+ }
+
+ mUpdateAppPackageName = mConfigHelper.getUpdateAppPackageName();
+ mDataAppPackageName = mConfigHelper.getDataAppPackageName();
+ mCheckTimeAllowedMillis = mConfigHelper.getCheckTimeAllowedMillis();
+ mFailedCheckRetryCount = mConfigHelper.getFailedCheckRetryCount();
+
+ // Validate the device configuration including the application packages.
+ // The manifest entries in the apps themselves are not validated until use as they can
+ // change and we don't want to prevent the system server starting due to a bad application.
+ throwIfDeviceSettingsOrAppsAreBad();
+
+ // Explicitly start in a reliability state where reliability triggering will do something.
+ mCheckTriggered = false;
+ mCheckFailureCount = 0;
+
+ // Initialize the intent helper.
+ mIntentHelper.initialize(mUpdateAppPackageName, mDataAppPackageName, this);
+
+ // Enable the reliability triggering so we will have at least one reliability trigger if
+ // a package isn't updated.
+ mIntentHelper.enableReliabilityTriggering();
+
+ Slog.i(TAG, "Time zone updater / data package tracking enabled");
+ }
+
+ /**
+ * Performs checks that confirm the system image has correctly configured package
+ * tracking configuration. Only called if package tracking is enabled. Throws an exception if
+ * the device is configured badly which will prevent the device booting.
+ */
+ private void throwIfDeviceSettingsOrAppsAreBad() {
+ // None of the checks below can be based on application manifest settings, otherwise a bad
+ // update could leave the device in an unbootable state. See validateDataAppManifest() and
+ // validateUpdaterAppManifest() for softer errors.
+
+ throwRuntimeExceptionIfNullOrEmpty(
+ mUpdateAppPackageName, "Update app package name missing.");
+ throwRuntimeExceptionIfNullOrEmpty(mDataAppPackageName, "Data app package name missing.");
+ if (mFailedCheckRetryCount < 1) {
+ throw logAndThrowRuntimeException("mFailedRetryCount=" + mFailedCheckRetryCount, null);
+ }
+ if (mCheckTimeAllowedMillis < 1000) {
+ throw logAndThrowRuntimeException(
+ "mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis, null);
+ }
+
+ // Validate the updater application package.
+ // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app
+ // after it is replaced by one in data so this check fails. http://b/35995024
+ // try {
+ // if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) {
+ // throw failWithException(
+ // "Update app " + mUpdateAppPackageName + " must be a priv-app.", null);
+ // }
+ // } catch (PackageManager.NameNotFoundException e) {
+ // throw failWithException("Could not determine update app package details for "
+ // + mUpdateAppPackageName, e);
+ // }
+ // TODO(nfuller) Consider permission checks. While an updated system app retains permissions
+ // obtained by the system version it's not clear how to check them.
+ Slog.d(TAG, "Update app " + mUpdateAppPackageName + " is valid.");
+
+ // Validate the data application package.
+ // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app
+ // after it is replaced by one in data. http://b/35995024
+ // try {
+ // if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) {
+ // throw failWithException(
+ // "Data app " + mDataAppPackageName + " must be a priv-app.", null);
+ // }
+ // } catch (PackageManager.NameNotFoundException e) {
+ // throw failWithException("Could not determine data app package details for "
+ // + mDataAppPackageName, e);
+ // }
+ // TODO(nfuller) Consider permission checks. While an updated system app retains permissions
+ // obtained by the system version it's not clear how to check them.
+ Slog.d(TAG, "Data app " + mDataAppPackageName + " is valid.");
+ }
+
+ /**
+ * Inspects the current in-memory state, installed packages and storage state to determine if an
+ * update check is needed and then trigger if it is.
+ *
+ * @param packageChanged true if this method was called because a known packaged definitely
+ * changed, false if the cause is a reliability trigger
+ */
+ @Override
+ public synchronized void triggerUpdateIfNeeded(boolean packageChanged) {
+ if (!mTrackingEnabled) {
+ throw new IllegalStateException("Unexpected call. Tracking is disabled.");
+ }
+
+ // Validate the applications' current manifest entries: make sure they are configured as
+ // they should be. These are not fatal and just means that no update is triggered: we don't
+ // want to take down the system server if an OEM or Google have pushed a bad update to
+ // an application.
+ boolean updaterAppManifestValid = validateUpdaterAppManifest();
+ boolean dataAppManifestValid = validateDataAppManifest();
+ if (!updaterAppManifestValid || !dataAppManifestValid) {
+ Slog.e(TAG, "No update triggered due to invalid application manifest entries."
+ + " updaterApp=" + updaterAppManifestValid
+ + ", dataApp=" + dataAppManifestValid);
+
+ // There's no point in doing reliability checks if the current packages are bad.
+ mIntentHelper.disableReliabilityTriggering();
+ return;
+ }
+
+ if (!packageChanged) {
+ // This call was made because the device is doing a "reliability" check.
+ // 4 possible cases:
+ // 1) No check has previously triggered since restart. We want to trigger in this case.
+ // 2) A check has previously triggered and it is in progress. We want to trigger if
+ // the response is overdue.
+ // 3) A check has previously triggered and it failed. We want to trigger, but only if
+ // we're not in a persistent failure state.
+ // 4) A check has previously triggered and it succeeded.
+ // We don't want to trigger, and want to stop future triggers.
+
+ if (!mCheckTriggered) {
+ // Case 1.
+ Slog.d(TAG, "triggerUpdateIfNeeded: First reliability trigger.");
+ } else if (isCheckInProgress()) {
+ // Case 2.
+ if (!isCheckResponseOverdue()) {
+ // A check is in progress but hasn't been given time to succeed.
+ Slog.d(TAG,
+ "triggerUpdateIfNeeded: checkComplete call is not yet overdue."
+ + " Not triggering.");
+ // Not doing any work, but also not disabling future reliability triggers.
+ return;
+ }
+ } else if (mCheckFailureCount > mFailedCheckRetryCount) {
+ // Case 3. If the system is in some kind of persistent failure state we don't want
+ // to keep checking, so just stop.
+ Slog.i(TAG, "triggerUpdateIfNeeded: number of allowed consecutive check failures"
+ + " exceeded. Stopping reliability triggers until next reboot or package"
+ + " update.");
+ mIntentHelper.disableReliabilityTriggering();
+ return;
+ } else if (mCheckFailureCount == 0) {
+ // Case 4.
+ Slog.i(TAG, "triggerUpdateIfNeeded: No reliability check required. Last check was"
+ + " successful.");
+ mIntentHelper.disableReliabilityTriggering();
+ return;
+ }
+ }
+
+ // Read the currently installed data / updater package versions.
+ PackageVersions currentInstalledVersions = lookupInstalledPackageVersions();
+ if (currentInstalledVersions == null) {
+ // This should not happen if the device is configured in a valid way.
+ Slog.e(TAG, "triggerUpdateIfNeeded: currentInstalledVersions was null");
+ mIntentHelper.disableReliabilityTriggering();
+ return;
+ }
+
+ // Establish the current state using package manager and stored state. Determine if we have
+ // already successfully checked the installed versions.
+ PackageStatus packageStatus = mPackageStatusStorage.getPackageStatus();
+ if (packageStatus == null) {
+ // This can imply corrupt, uninitialized storage state (e.g. first check ever on a
+ // device) or after some kind of reset.
+ Slog.i(TAG, "triggerUpdateIfNeeded: No package status data found. Data check needed.");
+ } else if (!packageStatus.mVersions.equals(currentInstalledVersions)) {
+ // The stored package version information differs from the installed version.
+ // Trigger the check in all cases.
+ Slog.i(TAG, "triggerUpdateIfNeeded: Stored package versions="
+ + packageStatus.mVersions + ", do not match current package versions="
+ + currentInstalledVersions + ". Triggering check.");
+ } else {
+ Slog.i(TAG, "triggerUpdateIfNeeded: Stored package versions match currently"
+ + " installed versions, currentInstalledVersions=" + currentInstalledVersions
+ + ", packageStatus.mCheckStatus=" + packageStatus.mCheckStatus);
+ if (packageStatus.mCheckStatus == PackageStatus.CHECK_COMPLETED_SUCCESS) {
+ // The last check succeeded and nothing has changed. Do nothing and disable
+ // reliability checks.
+ Slog.i(TAG, "triggerUpdateIfNeeded: Prior check succeeded. No need to trigger.");
+ mIntentHelper.disableReliabilityTriggering();
+ return;
+ }
+ }
+
+ // Generate a token to send to the updater app.
+ CheckToken checkToken =
+ mPackageStatusStorage.generateCheckToken(currentInstalledVersions);
+ if (checkToken == null) {
+ Slog.w(TAG, "triggerUpdateIfNeeded: Unable to generate check token."
+ + " Not sending check request.");
+ return;
+ }
+
+ // Trigger the update check.
+ mIntentHelper.sendTriggerUpdateCheck(checkToken);
+ mCheckTriggered = true;
+
+ // Update the reliability check state in case the update fails.
+ setCheckInProgress();
+
+ // Enable reliability triggering in case the check doesn't succeed and there is no
+ // response at all. Enabling reliability triggering is idempotent.
+ mIntentHelper.enableReliabilityTriggering();
+ }
+
+ /**
+ * Used to record the result of a check. Can be called even if active package tracking is
+ * disabled.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected synchronized void recordCheckResult(CheckToken checkToken, boolean success) {
+ Slog.i(TAG, "recordOperationResult: checkToken=" + checkToken + " success=" + success);
+
+ // If package tracking is disabled it means no record-keeping is required. However, we do
+ // want to clear out any stored state to make it clear that the current state is unknown and
+ // should tracking become enabled again (perhaps through an OTA) we'd need to perform an
+ // update check.
+ if (!mTrackingEnabled) {
+ // This means an updater has spontaneously modified time zone data without having been
+ // triggered. This can happen if the OEM is handling their own updates, but we don't
+ // need to do any tracking in this case.
+
+ if (checkToken == null) {
+ // This is the expected case if tracking is disabled but an OEM is handling time
+ // zone installs using their own mechanism.
+ Slog.d(TAG, "recordCheckResult: Tracking is disabled and no token has been"
+ + " provided. Resetting tracking state.");
+ } else {
+ // This is unexpected. If tracking is disabled then no check token should have been
+ // generated by the package tracker. An updater should never create its own token.
+ // This could be a bug in the updater.
+ Slog.w(TAG, "recordCheckResult: Tracking is disabled and a token " + checkToken
+ + " has been unexpectedly provided. Resetting tracking state.");
+ }
+ mPackageStatusStorage.resetCheckState();
+ return;
+ }
+
+ if (checkToken == null) {
+ /*
+ * If the checkToken is null it suggests an install / uninstall / acknowledgement has
+ * occurred without a prior trigger (or the client didn't return the token it was given
+ * for some reason, perhaps a bug).
+ *
+ * This shouldn't happen under normal circumstances:
+ *
+ * If package tracking is enabled, we assume it is the package tracker responsible for
+ * triggering updates and a token should have been produced and returned.
+ *
+ * If the OEM is handling time zone updates case package tracking should be disabled.
+ *
+ * This could happen in tests. The device should recover back to a known state by
+ * itself rather than be left in an invalid state.
+ *
+ * We treat this as putting the device into an unknown state and make sure that
+ * reliability triggering is enabled so we should recover.
+ */
+ Slog.i(TAG, "recordCheckResult: Unexpectedly missing checkToken, resetting"
+ + " storage state.");
+ mPackageStatusStorage.resetCheckState();
+
+ // Enable reliability triggering and reset the failure count so we know that the
+ // next reliability trigger will do something.
+ mIntentHelper.enableReliabilityTriggering();
+ mCheckFailureCount = 0;
+ } else {
+ // This is the expected case when tracking is enabled: a check was triggered and it has
+ // completed.
+ boolean recordedCheckCompleteSuccessfully =
+ mPackageStatusStorage.markChecked(checkToken, success);
+ if (recordedCheckCompleteSuccessfully) {
+ // If we have recorded the result (whatever it was) we know there is no check in
+ // progress.
+ setCheckComplete();
+
+ if (success) {
+ // Since the check was successful, no more reliability checks are required until
+ // there is a package change.
+ mIntentHelper.disableReliabilityTriggering();
+ mCheckFailureCount = 0;
+ } else {
+ // Enable reliability triggering to potentially check again in future.
+ mIntentHelper.enableReliabilityTriggering();
+ mCheckFailureCount++;
+ }
+ } else {
+ // The failure to record the check means an optimistic lock failure and suggests
+ // that another check was triggered after the token was generated.
+ Slog.i(TAG, "recordCheckResult: could not update token=" + checkToken
+ + " with success=" + success + ". Optimistic lock failure");
+
+ // Enable reliability triggering to potentially try again in future.
+ mIntentHelper.enableReliabilityTriggering();
+ mCheckFailureCount++;
+ }
+ }
+ }
+
+ /** Access to consecutive failure counts for use in tests. */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected int getCheckFailureCountForTests() {
+ return mCheckFailureCount;
+ }
+
+ private void setCheckInProgress() {
+ mLastTriggerTimestamp = mClockHelper.currentTimestamp();
+ }
+
+ private void setCheckComplete() {
+ mLastTriggerTimestamp = null;
+ }
+
+ private boolean isCheckInProgress() {
+ return mLastTriggerTimestamp != null;
+ }
+
+ private boolean isCheckResponseOverdue() {
+ if (mLastTriggerTimestamp == null) {
+ return false;
+ }
+ // Risk of overflow, but highly unlikely given the implementation and not problematic.
+ return mClockHelper.currentTimestamp() > mLastTriggerTimestamp + mCheckTimeAllowedMillis;
+ }
+
+ private PackageVersions lookupInstalledPackageVersions() {
+ int updatePackageVersion;
+ int dataPackageVersion;
+ try {
+ updatePackageVersion =
+ mPackageManagerHelper.getInstalledPackageVersion(mUpdateAppPackageName);
+ dataPackageVersion =
+ mPackageManagerHelper.getInstalledPackageVersion(mDataAppPackageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "lookupInstalledPackageVersions: Unable to resolve installed package"
+ + " versions", e);
+ return null;
+ }
+ return new PackageVersions(updatePackageVersion, dataPackageVersion);
+ }
+
+ private boolean validateDataAppManifest() {
+ // We only want to talk to a provider that exposed by the known data app package
+ // so we look up the providers exposed by that app and check the well-known authority is
+ // there. This prevents the case where *even if* the data app doesn't expose the provider
+ // required, another app cannot expose one to replace it.
+ if (!mPackageManagerHelper.contentProviderRegistered(
+ TimeZoneRulesDataContract.AUTHORITY, mDataAppPackageName)) {
+ // Error! Found the package but it didn't expose the correct provider.
+ Slog.w(TAG, "validateDataAppManifest: Data app " + mDataAppPackageName
+ + " does not expose the required provider with authority="
+ + TimeZoneRulesDataContract.AUTHORITY);
+ return false;
+ }
+ // TODO(nfuller) Add any permissions checks needed.
+ return true;
+ }
+
+ private boolean validateUpdaterAppManifest() {
+ try {
+ // The updater app is expected to have the UPDATE_TIME_ZONE_RULES permission.
+ // The updater app is expected to have a receiver for the intent we are going to trigger
+ // and require the TRIGGER_TIME_ZONE_RULES_CHECK.
+ if (!mPackageManagerHelper.usesPermission(
+ mUpdateAppPackageName,
+ RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION)) {
+ Slog.w(TAG, "validateUpdaterAppManifest: Updater app " + mDataAppPackageName
+ + " does not use permission="
+ + RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION);
+ return false;
+ }
+ if (!mPackageManagerHelper.receiverRegistered(
+ RulesUpdaterContract.createUpdaterIntent(mUpdateAppPackageName),
+ RulesUpdaterContract.TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION)) {
+ return false;
+ }
+
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "validateUpdaterAppManifest: Updater app " + mDataAppPackageName
+ + " does not expose the required broadcast receiver.", e);
+ return false;
+ }
+ }
+
+ private static void throwRuntimeExceptionIfNullOrEmpty(String value, String message) {
+ if (value == null || value.trim().isEmpty()) {
+ throw logAndThrowRuntimeException(message, null);
+ }
+ }
+
+ private static RuntimeException logAndThrowRuntimeException(String message, Throwable cause) {
+ Slog.wtf(TAG, message, cause);
+ throw new RuntimeException(message, cause);
+ }
+}
diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
new file mode 100644
index 0000000..2e0c21b
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import com.android.internal.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import java.util.List;
+
+/**
+ * A single class that implements multiple helper interfaces for use by {@link PackageTracker}.
+ */
+final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, PackageManagerHelper {
+
+ private static final String TAG = "PackageTrackerHelperImpl";
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ PackageTrackerHelperImpl(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public boolean isTrackingEnabled() {
+ return mContext.getResources().getBoolean(R.bool.config_timeZoneRulesUpdateTrackingEnabled);
+ }
+
+ @Override
+ public String getUpdateAppPackageName() {
+ return mContext.getResources().getString(R.string.config_timeZoneRulesUpdaterPackage);
+ }
+
+ @Override
+ public String getDataAppPackageName() {
+ Resources resources = mContext.getResources();
+ return resources.getString(R.string.config_timeZoneRulesDataPackage);
+ }
+
+ @Override
+ public int getCheckTimeAllowedMillis() {
+ return mContext.getResources().getInteger(
+ R.integer.config_timeZoneRulesCheckTimeMillisAllowed);
+ }
+
+ @Override
+ public int getFailedCheckRetryCount() {
+ return mContext.getResources().getInteger(R.integer.config_timeZoneRulesCheckRetryCount);
+ }
+
+ @Override
+ public long currentTimestamp() {
+ // Use of elapsedRealtime() because this is in-memory state and elapsedRealtime() shouldn't
+ // change if the system clock changes.
+ return SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public int getInstalledPackageVersion(String packageName)
+ throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ return packageInfo.versionCode;
+ }
+
+ @Override
+ public boolean isPrivilegedApp(String packageName) throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ return packageInfo.applicationInfo.isPrivilegedApp();
+ }
+
+ @Override
+ public boolean usesPermission(String packageName, String requiredPermissionName)
+ throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.GET_PERMISSIONS;
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ if (packageInfo.requestedPermissions == null) {
+ return false;
+ }
+ for (String requestedPermission : packageInfo.requestedPermissions) {
+ if (requiredPermissionName.equals(requestedPermission)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean contentProviderRegistered(String authority, String requiredPackageName) {
+ int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ ProviderInfo providerInfo =
+ mPackageManager.resolveContentProvider(authority, flags);
+ if (providerInfo == null) {
+ Slog.i(TAG, "contentProviderRegistered: No content provider registered with authority="
+ + authority);
+ return false;
+ }
+ boolean packageMatches =
+ requiredPackageName.equals(providerInfo.applicationInfo.packageName);
+ if (!packageMatches) {
+ Slog.i(TAG, "contentProviderRegistered: App with packageName=" + requiredPackageName
+ + " does not expose the a content provider with authority=" + authority);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean receiverRegistered(Intent intent, String requiredPermissionName)
+ throws PackageManager.NameNotFoundException {
+
+ int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceivers(intent, flags);
+ if (resolveInfo.size() != 1) {
+ Slog.i(TAG, "receiverRegistered: Zero or multiple broadcast receiver registered for"
+ + " intent=" + intent + ", found=" + resolveInfo);
+ return false;
+ }
+
+ ResolveInfo matched = resolveInfo.get(0);
+ boolean requiresPermission = requiredPermissionName.equals(matched.activityInfo.permission);
+ if (!requiresPermission) {
+ Slog.i(TAG, "receiverRegistered: Broadcast receiver registered for intent="
+ + intent + " must require permission " + requiredPermissionName);
+ }
+ return requiresPermission;
+ }
+}
diff --git a/services/core/java/com/android/server/timezone/PackageVersions.java b/services/core/java/com/android/server/timezone/PackageVersions.java
new file mode 100644
index 0000000..fc0d6e1
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/PackageVersions.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+/**
+ * Package version information about the time zone updater and time zone data application packages.
+ */
+final class PackageVersions {
+
+ final int mUpdateAppVersion;
+ final int mDataAppVersion;
+
+ PackageVersions(int updateAppVersion, int dataAppVersion) {
+ this.mUpdateAppVersion = updateAppVersion;
+ this.mDataAppVersion = dataAppVersion;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ PackageVersions that = (PackageVersions) o;
+
+ if (mUpdateAppVersion != that.mUpdateAppVersion) {
+ return false;
+ }
+ return mDataAppVersion == that.mDataAppVersion;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mUpdateAppVersion;
+ result = 31 * result + mDataAppVersion;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "PackageVersions{" +
+ "mUpdateAppVersion=" + mUpdateAppVersion +
+ ", mDataAppVersion=" + mDataAppVersion +
+ '}';
+ }
+}
diff --git a/core/java/android/app/CompatibilityDisplayProperties.aidl b/services/core/java/com/android/server/timezone/PermissionHelper.java
similarity index 69%
copy from core/java/android/app/CompatibilityDisplayProperties.aidl
copy to services/core/java/com/android/server/timezone/PermissionHelper.java
index 626a63e..ba91c7f 100644
--- a/core/java/android/app/CompatibilityDisplayProperties.aidl
+++ b/services/core/java/com/android/server/timezone/PermissionHelper.java
@@ -14,7 +14,12 @@
* limitations under the License.
*/
-package android.app;
+package com.android.server.timezone;
-/** @hide */
-parcelable CompatibilityDisplayProperties;
+/**
+ * An easy-to-mock interface around permission checks for use by {@link RulesManagerService}.
+ */
+public interface PermissionHelper {
+
+ void enforceCallerHasPermission(String requiredPermission) throws SecurityException;
+}
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
new file mode 100644
index 0000000..82bd356
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+import android.app.timezone.Callback;
+import android.app.timezone.DistroFormatVersion;
+import android.app.timezone.DistroRulesVersion;
+import android.app.timezone.ICallback;
+import android.app.timezone.IRulesManager;
+import android.app.timezone.RulesManager;
+import android.app.timezone.RulesState;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import libcore.tzdata.shared2.DistroException;
+import libcore.tzdata.shared2.DistroVersion;
+import libcore.tzdata.shared2.StagedDistroOperation;
+import libcore.tzdata.update2.TimeZoneDistroInstaller;
+
+// TODO(nfuller) Add EventLog calls where useful in the system server.
+// TODO(nfuller) Check logging best practices in the system server.
+// TODO(nfuller) Check error handling best practices in the system server.
+public final class RulesManagerService extends IRulesManager.Stub {
+
+ private static final String TAG = "timezone.RulesManagerService";
+
+ /** The distro format supported by this device. */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ static final DistroFormatVersion DISTRO_FORMAT_VERSION_SUPPORTED =
+ new DistroFormatVersion(
+ DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
+ DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
+
+ public static class Lifecycle extends SystemService {
+ private RulesManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = RulesManagerService.create(getContext());
+ mService.start();
+
+ publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, mService);
+ }
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ static final String REQUIRED_UPDATER_PERMISSION =
+ android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
+ private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
+ private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
+
+ private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
+ private final PermissionHelper mPermissionHelper;
+ private final PackageTracker mPackageTracker;
+ private final Executor mExecutor;
+ private final TimeZoneDistroInstaller mInstaller;
+ private final FileDescriptorHelper mFileDescriptorHelper;
+
+ private static RulesManagerService create(Context context) {
+ RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
+ return new RulesManagerService(
+ helper /* permissionHelper */,
+ helper /* executor */,
+ helper /* fileDescriptorHelper */,
+ PackageTracker.create(context),
+ new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
+ }
+
+ // A constructor that can be used by tests to supply mocked / faked dependencies.
+ RulesManagerService(PermissionHelper permissionHelper,
+ Executor executor,
+ FileDescriptorHelper fileDescriptorHelper, PackageTracker packageTracker,
+ TimeZoneDistroInstaller timeZoneDistroInstaller) {
+ mPermissionHelper = permissionHelper;
+ mExecutor = executor;
+ mFileDescriptorHelper = fileDescriptorHelper;
+ mPackageTracker = packageTracker;
+ mInstaller = timeZoneDistroInstaller;
+ }
+
+ public void start() {
+ mPackageTracker.start();
+ }
+
+ @Override // Binder call
+ public RulesState getRulesState() {
+ mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
+
+ synchronized(this) {
+ String systemRulesVersion;
+ try {
+ systemRulesVersion = mInstaller.getSystemRulesVersion();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read system rules", e);
+ return null;
+ }
+
+ boolean operationInProgress = this.mOperationInProgress.get();
+
+ // Determine the staged operation status, if possible.
+ DistroRulesVersion stagedDistroRulesVersion = null;
+ int stagedOperationStatus = RulesState.STAGED_OPERATION_UNKNOWN;
+ if (!operationInProgress) {
+ StagedDistroOperation stagedDistroOperation;
+ try {
+ stagedDistroOperation = mInstaller.getStagedDistroOperation();
+ if (stagedDistroOperation == null) {
+ stagedOperationStatus = RulesState.STAGED_OPERATION_NONE;
+ } else if (stagedDistroOperation.isUninstall) {
+ stagedOperationStatus = RulesState.STAGED_OPERATION_UNINSTALL;
+ } else {
+ // Must be an install.
+ stagedOperationStatus = RulesState.STAGED_OPERATION_INSTALL;
+ DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
+ stagedDistroRulesVersion = new DistroRulesVersion(
+ stagedDistroVersion.rulesVersion,
+ stagedDistroVersion.revision);
+ }
+ } catch (DistroException | IOException e) {
+ Slog.w(TAG, "Failed to read staged distro.", e);
+ }
+ }
+
+ // Determine the installed distro state, if possible.
+ DistroVersion installedDistroVersion;
+ int distroStatus = RulesState.DISTRO_STATUS_UNKNOWN;
+ DistroRulesVersion installedDistroRulesVersion = null;
+ if (!operationInProgress) {
+ try {
+ installedDistroVersion = mInstaller.getInstalledDistroVersion();
+ if (installedDistroVersion == null) {
+ distroStatus = RulesState.DISTRO_STATUS_NONE;
+ installedDistroRulesVersion = null;
+ } else {
+ distroStatus = RulesState.DISTRO_STATUS_INSTALLED;
+ installedDistroRulesVersion = new DistroRulesVersion(
+ installedDistroVersion.rulesVersion,
+ installedDistroVersion.revision);
+ }
+ } catch (DistroException | IOException e) {
+ Slog.w(TAG, "Failed to read installed distro.", e);
+ }
+ }
+ return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
+ operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
+ distroStatus, installedDistroRulesVersion);
+ }
+ }
+
+ @Override
+ public int requestInstall(
+ ParcelFileDescriptor timeZoneDistro, byte[] checkTokenBytes, ICallback callback) {
+ mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
+
+ CheckToken checkToken = null;
+ if (checkTokenBytes != null) {
+ checkToken = createCheckTokenOrThrow(checkTokenBytes);
+ }
+ synchronized (this) {
+ if (timeZoneDistro == null) {
+ throw new NullPointerException("timeZoneDistro == null");
+ }
+ if (callback == null) {
+ throw new NullPointerException("observer == null");
+ }
+ if (mOperationInProgress.get()) {
+ return RulesManager.ERROR_OPERATION_IN_PROGRESS;
+ }
+ mOperationInProgress.set(true);
+
+ // Execute the install asynchronously.
+ mExecutor.execute(new InstallRunnable(timeZoneDistro, checkToken, callback));
+
+ return RulesManager.SUCCESS;
+ }
+ }
+
+ private class InstallRunnable implements Runnable {
+
+ private final ParcelFileDescriptor mTimeZoneDistro;
+ private final CheckToken mCheckToken;
+ private final ICallback mCallback;
+
+ InstallRunnable(
+ ParcelFileDescriptor timeZoneDistro, CheckToken checkToken, ICallback callback) {
+ mTimeZoneDistro = timeZoneDistro;
+ mCheckToken = checkToken;
+ mCallback = callback;
+ }
+
+ @Override
+ public void run() {
+ // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
+ // when we are done.
+ boolean success = false;
+ try {
+ byte[] distroBytes =
+ RulesManagerService.this.mFileDescriptorHelper.readFully(mTimeZoneDistro);
+ int installerResult = mInstaller.stageInstallWithErrorCode(distroBytes);
+ int resultCode = mapInstallerResultToApiCode(installerResult);
+ sendFinishedStatus(mCallback, resultCode);
+
+ // All the installer failure modes are currently non-recoverable and won't be
+ // improved by trying again. Therefore success = true.
+ success = true;
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to install distro.", e);
+ sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
+ } finally {
+ // Notify the package tracker that the operation is now complete.
+ mPackageTracker.recordCheckResult(mCheckToken, success);
+
+ mOperationInProgress.set(false);
+ }
+ }
+
+ private int mapInstallerResultToApiCode(int installerResult) {
+ switch (installerResult) {
+ case TimeZoneDistroInstaller.INSTALL_SUCCESS:
+ return Callback.SUCCESS;
+ case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
+ return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
+ case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
+ return Callback.ERROR_INSTALL_RULES_TOO_OLD;
+ case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
+ return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
+ case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
+ return Callback.ERROR_INSTALL_VALIDATION_ERROR;
+ default:
+ return Callback.ERROR_UNKNOWN_FAILURE;
+ }
+ }
+ }
+
+ @Override
+ public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
+ mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
+
+ CheckToken checkToken = null;
+ if (checkTokenBytes != null) {
+ checkToken = createCheckTokenOrThrow(checkTokenBytes);
+ }
+ synchronized(this) {
+ if (callback == null) {
+ throw new NullPointerException("callback == null");
+ }
+
+ if (mOperationInProgress.get()) {
+ return RulesManager.ERROR_OPERATION_IN_PROGRESS;
+ }
+ mOperationInProgress.set(true);
+
+ // Execute the uninstall asynchronously.
+ mExecutor.execute(new UninstallRunnable(checkToken, callback));
+
+ return RulesManager.SUCCESS;
+ }
+ }
+
+ private class UninstallRunnable implements Runnable {
+
+ private final CheckToken mCheckToken;
+ private final ICallback mCallback;
+
+ public UninstallRunnable(CheckToken checkToken, ICallback callback) {
+ mCheckToken = checkToken;
+ mCallback = callback;
+ }
+
+ @Override
+ public void run() {
+ boolean success = false;
+ try {
+ success = mInstaller.stageUninstall();
+ // Right now we just have success (0) / failure (1). All clients should be checking
+ // against SUCCESS. More granular failures may be added in future.
+ int resultCode = success ? Callback.SUCCESS
+ : Callback.ERROR_UNKNOWN_FAILURE;
+ sendFinishedStatus(mCallback, resultCode);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to uninstall distro.", e);
+ sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
+ } finally {
+ // Notify the package tracker that the operation is now complete.
+ mPackageTracker.recordCheckResult(mCheckToken, success);
+
+ mOperationInProgress.set(false);
+ }
+ }
+ }
+
+ private void sendFinishedStatus(ICallback callback, int resultCode) {
+ try {
+ callback.onFinished(resultCode);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify observer of result", e);
+ }
+ }
+
+ @Override
+ public void requestNothing(byte[] checkTokenBytes, boolean success) {
+ mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
+ CheckToken checkToken = null;
+ if (checkTokenBytes != null) {
+ checkToken = createCheckTokenOrThrow(checkTokenBytes);
+ }
+ mPackageTracker.recordCheckResult(checkToken, success);
+ }
+
+ private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
+ CheckToken checkToken;
+ try {
+ checkToken = CheckToken.fromByteArray(checkTokenBytes);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Unable to read token bytes "
+ + Arrays.toString(checkTokenBytes), e);
+ }
+ return checkToken;
+ }
+}
diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
new file mode 100644
index 0000000..15a571d
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import libcore.io.Streams;
+
+/**
+ * A single class that implements multiple helper interfaces for use by {@link RulesManagerService}.
+ */
+final class RulesManagerServiceHelperImpl
+ implements PermissionHelper, Executor, FileDescriptorHelper {
+
+ private final Context mContext;
+
+ RulesManagerServiceHelperImpl(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void enforceCallerHasPermission(String requiredPermission) {
+ mContext.enforceCallingPermission(requiredPermission, null /* message */);
+ }
+
+ // TODO Wake lock required?
+ @Override
+ public void execute(Runnable runnable) {
+ // TODO Is there a better way?
+ new Thread(runnable).start();
+ }
+
+ @Override
+ public byte[] readFully(ParcelFileDescriptor parcelFileDescriptor) throws IOException {
+ try (ParcelFileDescriptor pfd = parcelFileDescriptor) {
+ // Read bytes
+ FileInputStream in = new FileInputStream(pfd.getFileDescriptor(), false /* isOwner */);
+ return Streams.readFully(in);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index cc4c23d..c6393e7 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -67,6 +67,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -326,8 +327,6 @@
agentInfo.label = resolveInfo.loadLabel(pm);
agentInfo.icon = resolveInfo.loadIcon(pm);
agentInfo.settings = getSettingsAttrs(pm, resolveInfo);
- agentInfo.agent = new TrustAgentWrapper(mContext, this,
- new Intent().setComponent(name), userInfo.getUserHandle());
} else {
int index = mActiveAgents.indexOf(agentInfo);
agentInfo = mActiveAgents.valueAt(index);
@@ -364,6 +363,11 @@
}
}
+ if (agentInfo.agent == null) {
+ agentInfo.agent = new TrustAgentWrapper(mContext, this,
+ new Intent().setComponent(name), userInfo.getUserHandle());
+ }
+
if (!mActiveAgents.contains(agentInfo)) {
mActiveAgents.add(agentInfo);
} else {
@@ -575,20 +579,22 @@
}
private void maybeEnableFactoryTrustAgents(LockPatternUtils utils, int userId) {
- if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) {
- return;
- }
- PackageManager pm = mContext.getPackageManager();
- List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userId);
ComponentName defaultAgent = getDefaultFactoryTrustAgent(mContext);
boolean shouldUseDefaultAgent = defaultAgent != null;
- ArraySet<ComponentName> discoveredAgents = new ArraySet<>();
if (shouldUseDefaultAgent) {
- discoveredAgents.add(defaultAgent);
Log.i(TAG, "Enabling " + defaultAgent + " because it is a default agent.");
+ utils.setEnabledTrustAgents(Collections.singleton(defaultAgent), userId);
} else { // A default agent is not set; perform regular trust agent discovery
+ if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) {
+ return;
+ }
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userId);
+
+ ArraySet<ComponentName> discoveredAgents = new ArraySet<>();
+
for (ResolveInfo resolveInfo : resolveInfos) {
ComponentName componentName = getComponentName(resolveInfo);
int applicationInfoFlags = resolveInfo.serviceInfo.applicationInfo.flags;
@@ -599,13 +605,13 @@
}
discoveredAgents.add(componentName);
}
- }
- List<ComponentName> previouslyEnabledAgents = utils.getEnabledTrustAgents(userId);
- if (previouslyEnabledAgents != null) {
- discoveredAgents.addAll(previouslyEnabledAgents);
+ List<ComponentName> previouslyEnabledAgents = utils.getEnabledTrustAgents(userId);
+ if (previouslyEnabledAgents != null) {
+ discoveredAgents.addAll(previouslyEnabledAgents);
+ }
+ utils.setEnabledTrustAgents(discoveredAgents, userId);
}
- utils.setEnabledTrustAgents(discoveredAgents, userId);
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, userId);
}
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 63c6195..1f75640 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -16,7 +16,7 @@
package com.android.server.vr;
import android.annotation.NonNull;
-import android.app.CompatibilityDisplayProperties;
+import android.app.Vr2dDisplayProperties;
import android.content.ComponentName;
import android.service.vr.IPersistentVrStateCallbacks;
@@ -83,16 +83,16 @@
public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId);
/**
- * Sets the resolution and DPI of the compatibility virtual display used to display
+ * Sets the resolution and DPI of the vr2d virtual display used to display
* 2D applications in VR mode.
*
* <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p>
*
- * @param compatDisplayProp Properties of the virtual display for 2D applications
+ * @param vr2dDisplayProp Properties of the virtual display for 2D applications
* in VR mode.
*/
- public abstract void setCompatibilityDisplayProperties(
- CompatibilityDisplayProperties compatDisplayProp);
+ public abstract void setVr2dDisplayProperties(
+ Vr2dDisplayProperties vr2dDisplayProp);
/**
* Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will
@@ -110,7 +110,7 @@
* @return {@link android.view.Display.INVALID_DISPLAY} if there is no virtual display
* currently, else return the display id of the virtual display
*/
- public abstract int getCompatibilityDisplayId();
+ public abstract int getVr2dDisplayId();
/**
* Adds listener that reports state changes to persistent VR mode.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 860b241..0e183f0 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -21,7 +21,7 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.CompatibilityDisplayProperties;
+import android.app.Vr2dDisplayProperties;
import android.app.NotificationManager;
import android.annotation.NonNull;
import android.content.ComponentName;
@@ -141,7 +141,7 @@
private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
/** Tracks the state of the screen and keyguard UI.*/
private int mSystemSleepFlags = FLAG_AWAKE;
- private CompatibilityDisplay mCompatibilityDisplay;
+ private Vr2dDisplay mVr2dDisplay;
private static final int MSG_VR_STATE_CHANGE = 0;
private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
@@ -428,15 +428,15 @@
}
@Override
- public void setCompatibilityDisplayProperties(
- CompatibilityDisplayProperties compatDisplayProp) {
+ public void setVr2dDisplayProperties(
+ Vr2dDisplayProperties vr2dDisplayProp) {
enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS);
- VrManagerService.this.setCompatibilityDisplayProperties(compatDisplayProp);
+ VrManagerService.this.setVr2dDisplayProperties(vr2dDisplayProp);
}
@Override
- public int getCompatibilityDisplayId() {
- return VrManagerService.this.getCompatibilityDisplayId();
+ public int getVr2dDisplayId() {
+ return VrManagerService.this.getVr2dDisplayId();
}
@Override
@@ -549,14 +549,14 @@
}
@Override
- public void setCompatibilityDisplayProperties(
- CompatibilityDisplayProperties compatDisplayProp) {
- VrManagerService.this.setCompatibilityDisplayProperties(compatDisplayProp);
+ public void setVr2dDisplayProperties(
+ Vr2dDisplayProperties compatDisplayProp) {
+ VrManagerService.this.setVr2dDisplayProperties(compatDisplayProp);
}
@Override
- public int getCompatibilityDisplayId() {
- return VrManagerService.this.getCompatibilityDisplayId();
+ public int getVr2dDisplayId() {
+ return VrManagerService.this.getVr2dDisplayId();
}
@Override
@@ -608,8 +608,8 @@
DisplayManager dm =
(DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
- mCompatibilityDisplay = new CompatibilityDisplay(dm, ami, mVrManager);
- mCompatibilityDisplay.init(getContext());
+ mVr2dDisplay = new Vr2dDisplay(dm, ami, mVrManager);
+ mVr2dDisplay.init(getContext());
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
mVrModeAllowed = true;
@@ -1116,20 +1116,20 @@
}
}
- public void setCompatibilityDisplayProperties(
- CompatibilityDisplayProperties compatDisplayProp) {
- if (mCompatibilityDisplay != null) {
- mCompatibilityDisplay.setVirtualDisplayProperties(compatDisplayProp);
+ public void setVr2dDisplayProperties(
+ Vr2dDisplayProperties compatDisplayProp) {
+ if (mVr2dDisplay != null) {
+ mVr2dDisplay.setVirtualDisplayProperties(compatDisplayProp);
return;
}
- Slog.w(TAG, "CompatibilityDisplay is null!");
+ Slog.w(TAG, "Vr2dDisplay is null!");
}
- private int getCompatibilityDisplayId() {
- if (mCompatibilityDisplay != null) {
- return mCompatibilityDisplay.getVirtualDisplayId();
+ private int getVr2dDisplayId() {
+ if (mVr2dDisplay != null) {
+ return mVr2dDisplay.getVirtualDisplayId();
}
- Slog.w(TAG, "CompatibilityDisplay is null!");
+ Slog.w(TAG, "Vr2dDisplay is null!");
return INVALID_DISPLAY;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index c625cbe..b5e194b 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -36,7 +36,6 @@
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Trace;
import android.util.Slog;
import android.view.IApplicationToken;
@@ -319,7 +318,7 @@
+ " token: " + mToken);
return;
}
- mContainer.setDisablePreviewSnapshots(disable);
+ mContainer.setDisablePreviewScreenshots(disable);
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 36418be..640bac2 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -23,6 +23,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -48,6 +49,7 @@
import static com.android.server.wm.WindowManagerService.logWithStack;
import android.annotation.NonNull;
+import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -1528,12 +1530,24 @@
return candidate;
}
- void setDisablePreviewSnapshots(boolean disable) {
+ /**
+ * See {@link Activity#setDisablePreviewScreenshots}.
+ */
+ void setDisablePreviewScreenshots(boolean disable) {
mDisbalePreviewScreenshots = disable;
}
- boolean shouldDisablePreviewScreenshots() {
- return mDisbalePreviewScreenshots;
+ /**
+ * Retrieves whether we'd like to generate a snapshot that's based solely on the theme. This is
+ * the case when preview screenshots are disabled {@link #setDisablePreviewScreenshots} or when
+ * we can't take a snapshot for other reasons, for example, if we have a secure window.
+ *
+ * @return True if we need to generate an app theme snapshot, false if we'd like to take a real
+ * screenshot.
+ */
+ boolean shouldUseAppThemeSnapshot() {
+ return mDisbalePreviewScreenshots || forAllWindows(w -> (w.mAttrs.flags & FLAG_SECURE) != 0,
+ true /* topToBottom */);
}
@Override
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 4100446..d44cd13 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -188,6 +188,7 @@
boolean animateDimLayers() {
int fullScreen = -1;
int fullScreenAndDimming = -1;
+ int topFullScreenUserLayer = 0;
boolean result = false;
for (int i = mState.size() - 1; i >= 0; i--) {
@@ -213,8 +214,18 @@
// and we have to make sure we always animate the layer.
if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
fullScreen = i;
- if (mState.valueAt(i).continueDimming) {
+ if (!state.continueDimming) {
+ continue;
+ }
+
+ // When choosing which user to assign the shared fullscreen layer to
+ // we need to look at Z-order.
+ if (topFullScreenUserLayer == 0 ||
+ (state.animator != null && state.animator.mAnimLayer > topFullScreenUserLayer)) {
fullScreenAndDimming = i;
+ if (state.animator != null) {
+ topFullScreenUserLayer = state.animator.mAnimLayer;
+ }
}
} else {
// We always want to animate the non fullscreen windows, they don't share their
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index be242b6..b27d6c4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2891,21 +2891,6 @@
final Rect frame = new Rect();
final Rect stackBounds = new Rect();
- boolean includeImeInScreenshot;
- synchronized(mService.mWindowMap) {
- final AppWindowToken imeTargetAppToken = mService.mInputMethodTarget != null
- ? mService.mInputMethodTarget.mAppToken : null;
- // We only include the Ime in the screenshot if the app we are screenshoting is the IME
- // target and isn't in multi-window mode. We don't screenshot the IME in multi-window
- // mode because the frame of the IME might not overlap with that of the app.
- // E.g. IME target app at the top in split-screen mode and the IME at the bottom
- // overlapping with the bottom app.
- includeImeInScreenshot = imeTargetAppToken != null
- && imeTargetAppToken.appToken != null
- && imeTargetAppToken.appToken.asBinder() == appToken
- && !mService.mInputMethodTarget.isInMultiWindowMode();
- }
-
final int aboveAppLayer = (mService.mPolicy.getWindowLayerFromTypeLw(TYPE_APPLICATION) + 1)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
final MutableBoolean mutableIncludeFullDisplay = new MutableBoolean(includeFullDisplay);
@@ -2923,9 +2908,7 @@
return false;
}
if (w.mIsImWindow) {
- if (!includeImeInScreenshot) {
- return false;
- }
+ return false;
} else if (w.mIsWallpaper) {
// If this is the wallpaper layer and we're only looking for the wallpaper layer
// then the target window state is this one.
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index fbb826d..b79173c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -31,11 +31,13 @@
import android.graphics.Rect;
import android.os.Environment;
import android.util.ArraySet;
+import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerPolicy.StartingSurface;
import com.google.android.collect.Sets;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
import java.io.PrintWriter;
@@ -206,7 +208,7 @@
final AppWindowToken topChild = task.getTopChild();
if (StackId.isHomeOrRecentsStack(task.mStack.mStackId)) {
return SNAPSHOT_MODE_NONE;
- } else if (topChild != null && topChild.shouldDisablePreviewScreenshots()) {
+ } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
return SNAPSHOT_MODE_APP_THEME;
} else {
return SNAPSHOT_MODE_REAL;
@@ -227,6 +229,8 @@
return null;
}
final int color = task.getTaskDescription().getBackgroundColor();
+ final int statusBarColor = task.getTaskDescription().getStatusBarColor();
+ final int navigationBarColor = task.getTaskDescription().getNavigationBarColor();
final GraphicBuffer buffer = GraphicBuffer.create(mainWindow.getFrameLw().width(),
mainWindow.getFrameLw().height(),
RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_RARELY | USAGE_SW_READ_NEVER);
@@ -235,6 +239,11 @@
}
final Canvas c = buffer.lockCanvas();
c.drawColor(color);
+ final LayoutParams attrs = mainWindow.getAttrs();
+ final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
+ attrs.privateFlags, attrs.systemUiVisibility, statusBarColor, navigationBarColor);
+ decorPainter.setInsets(mainWindow.mContentInsets, mainWindow.mStableInsets);
+ decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
buffer.unlockCanvasAndPost(c);
return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
mainWindow.mStableInsets, false /* reduced */, 1.0f /* scale */);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index f2a92df..e5c7a72 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -24,6 +24,7 @@
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Bitmap.Config;
import android.os.Process;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -266,12 +267,13 @@
final File file = getBitmapFile(mTaskId, mUserId);
final File reducedFile = getReducedResolutionBitmapFile(mTaskId, mUserId);
final Bitmap bitmap = Bitmap.createHardwareBitmap(mSnapshot.getSnapshot());
- final Bitmap reduced = Bitmap.createScaledBitmap(bitmap,
+ final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
+ final Bitmap reduced = Bitmap.createScaledBitmap(swBitmap,
(int) (bitmap.getWidth() * REDUCED_SCALE),
(int) (bitmap.getHeight() * REDUCED_SCALE), true /* filter */);
try {
FileOutputStream fos = new FileOutputStream(file);
- bitmap.compress(JPEG, QUALITY, fos);
+ swBitmap.compress(JPEG, QUALITY, fos);
fos.close();
FileOutputStream reducedFos = new FileOutputStream(reducedFile);
reduced.compress(JPEG, QUALITY, reducedFos);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index c816ba3..2b9e800 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -42,8 +42,11 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.Nullable;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskSnapshot;
+import android.app.ActivityThread;
+import android.content.Context;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
import android.graphics.Paint;
@@ -118,13 +121,8 @@
private final Handler mHandler;
private boolean mSizeMismatch;
private final Paint mBackgroundPaint = new Paint();
- private final Paint mStatusBarPaint = new Paint();
- private final Paint mNavigationBarPaint = new Paint();
private final int mStatusBarColor;
- private final int mNavigationBarColor;
- private final int mSysUiVis;
- private final int mWindowFlags;
- private final int mWindowPrivateFlags;
+ @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
TaskSnapshot snapshot) {
@@ -224,15 +222,9 @@
mTitle = title;
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mTaskBounds = taskBounds;
- mSysUiVis = sysUiVis;
- mWindowFlags = windowFlags;
- mWindowPrivateFlags = windowPrivateFlags;
- mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags,
- service.mContext.getColor(R.color.system_bar_background_semi_transparent),
- statusBarColor);
- mNavigationBarColor = navigationBarColor;
- mStatusBarPaint.setColor(mStatusBarColor);
- mNavigationBarPaint.setColor(navigationBarColor);
+ mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
+ windowPrivateFlags, sysUiVis, statusBarColor, navigationBarColor);
+ mStatusBarColor = statusBarColor;
}
@Override
@@ -258,6 +250,7 @@
mStableInsets.set(stableInsets);
mSizeMismatch = (mFrame.width() != mSnapshot.getSnapshot().getWidth()
|| mFrame.height() != mSnapshot.getSnapshot().getHeight());
+ mSystemBarBackgroundPainter.setInsets(contentInsets, stableInsets);
}
private void drawSnapshot() {
@@ -346,7 +339,7 @@
@VisibleForTesting
void drawBackgroundAndBars(Canvas c, Rect frame) {
- final int statusBarHeight = getStatusBarColorViewHeight();
+ final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
final boolean fillHorizontally = c.getWidth() > frame.right;
final boolean fillVertically = c.getHeight() > frame.bottom;
if (fillHorizontally) {
@@ -359,44 +352,7 @@
if (fillVertically) {
c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
}
- drawStatusBarBackground(c, frame, statusBarHeight);
- drawNavigationBarBackground(c);
- }
-
- private int getStatusBarColorViewHeight() {
- final boolean forceStatusBarBackground =
- (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
- if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) {
- return getColorViewTopInset(mStableInsets.top, mContentInsets.top);
- } else {
- return 0;
- }
- }
-
- private boolean isNavigationBarColorViewVisible() {
- return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */);
- }
-
- @VisibleForTesting
- void drawStatusBarBackground(Canvas c, Rect frame, int statusBarHeight) {
- if (statusBarHeight > 0 && c.getWidth() > frame.right) {
- final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
- mContentInsets.right);
- c.drawRect(frame.right, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
- }
- }
-
- @VisibleForTesting
- void drawNavigationBarBackground(Canvas c) {
- final Rect navigationBarRect = new Rect();
- getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
- navigationBarRect);
- final boolean visible = isNavigationBarColorViewVisible();
- if (visible && !navigationBarRect.isEmpty()) {
- c.drawRect(navigationBarRect, mNavigationBarPaint);
- }
+ mSystemBarBackgroundPainter.drawDecors(c, frame);
}
private void reportDrawn() {
@@ -450,4 +406,84 @@
}
}
}
+
+ /**
+ * Helper class to draw the background of the system bars in regions the task snapshot isn't
+ * filling the window.
+ */
+ static class SystemBarBackgroundPainter {
+
+ private final Rect mContentInsets = new Rect();
+ private final Rect mStableInsets = new Rect();
+ private final Paint mStatusBarPaint = new Paint();
+ private final Paint mNavigationBarPaint = new Paint();
+ private final int mStatusBarColor;
+ private final int mNavigationBarColor;
+ private final int mWindowFlags;
+ private final int mWindowPrivateFlags;
+ private final int mSysUiVis;
+
+ SystemBarBackgroundPainter( int windowFlags, int windowPrivateFlags, int sysUiVis,
+ int statusBarColor, int navigationBarColor) {
+ mWindowFlags = windowFlags;
+ mWindowPrivateFlags = windowPrivateFlags;
+ mSysUiVis = sysUiVis;
+ final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
+ mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags,
+ context.getColor(R.color.system_bar_background_semi_transparent),
+ statusBarColor);
+ mNavigationBarColor = navigationBarColor;
+ mStatusBarPaint.setColor(mStatusBarColor);
+ mNavigationBarPaint.setColor(navigationBarColor);
+ }
+
+ void setInsets(Rect contentInsets, Rect stableInsets) {
+ mContentInsets.set(contentInsets);
+ mStableInsets.set(stableInsets);
+ }
+
+ int getStatusBarColorViewHeight() {
+ final boolean forceStatusBarBackground =
+ (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+ if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
+ mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) {
+ return getColorViewTopInset(mStableInsets.top, mContentInsets.top);
+ } else {
+ return 0;
+ }
+ }
+
+ private boolean isNavigationBarColorViewVisible() {
+ return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
+ mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */);
+ }
+
+ void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
+ drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
+ drawNavigationBarBackground(c);
+ }
+
+ @VisibleForTesting
+ void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
+ int statusBarHeight) {
+ if (statusBarHeight > 0
+ && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
+ final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
+ mContentInsets.right);
+ final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
+ c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
+ }
+ }
+
+ @VisibleForTesting
+ void drawNavigationBarBackground(Canvas c) {
+ final Rect navigationBarRect = new Rect();
+ getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
+ navigationBarRect);
+ final boolean visible = isNavigationBarColorViewVisible();
+ if (visible && !navigationBarRect.isEmpty()) {
+ c.drawRect(navigationBarRect, mNavigationBarPaint);
+ }
+ }
+ }
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0006110..14a2381 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -36,6 +36,7 @@
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/threads.h>
+#include <utils/SortedVector.h>
#include <input/PointerController.h>
#include <input/SpriteController.h>
@@ -206,6 +207,7 @@
void setInputDispatchMode(bool enabled, bool frozen);
void setSystemUiVisibility(int32_t visibility);
void setPointerSpeed(int32_t speed);
+ void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
void setShowTouches(bool enabled);
void setInteractive(bool interactive);
void reloadCalibration();
@@ -290,6 +292,9 @@
// Pointer controller singleton, created and destroyed as needed.
wp<PointerController> pointerController;
+
+ // Input devices to be disabled
+ SortedVector<int32_t> disabledInputDevices;
} mLocked;
std::atomic<bool> mInteractive;
@@ -475,6 +480,8 @@
outConfig->setDisplayInfo(false /*external*/, mLocked.internalViewport);
outConfig->setDisplayInfo(true /*external*/, mLocked.externalViewport);
+
+ outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
}
@@ -764,6 +771,24 @@
InputReaderConfiguration::CHANGE_POINTER_SPEED);
}
+void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ssize_t index = mLocked.disabledInputDevices.indexOf(deviceId);
+ bool currentlyEnabled = index < 0;
+ if (!enabled && currentlyEnabled) {
+ mLocked.disabledInputDevices.add(deviceId);
+ }
+ if (enabled && !currentlyEnabled) {
+ mLocked.disabledInputDevices.remove(deviceId);
+ }
+ } // release lock
+
+ mInputManager->getReader()->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_ENABLED_STATE);
+}
+
void NativeInputManager::setShowTouches(bool enabled) {
{ // acquire lock
AutoMutex _l(mLock);
@@ -1335,6 +1360,7 @@
static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */,
jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->getInputManager()->getReader()->toggleCapsLockState(deviceId);
}
@@ -1355,6 +1381,7 @@
static void nativeSetPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->setPointerCapture(enabled);
}
@@ -1416,6 +1443,7 @@
static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->reloadCalibration();
}
@@ -1482,13 +1510,36 @@
im->getInputManager()->getDispatcher()->monitor();
}
+static jboolean nativeIsInputDeviceEnabled(JNIEnv* env /* env */,
+ jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ return im->getInputManager()->getReader()->isInputDeviceEnabled(deviceId);
+}
+
+static void nativeEnableInputDevice(JNIEnv* /* env */,
+ jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setInputDeviceEnabled(deviceId, true);
+}
+
+static void nativeDisableInputDevice(JNIEnv* /* env */,
+ jclass /* clazz */, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setInputDeviceEnabled(deviceId, false);
+}
+
static void nativeSetPointerIconType(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint iconId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->setPointerIconType(iconId);
}
static void nativeReloadPointerIcons(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
im->reloadPointerIcons();
}
@@ -1576,6 +1627,12 @@
(void*) nativeDump },
{ "nativeMonitor", "(J)V",
(void*) nativeMonitor },
+ { "nativeIsInputDeviceEnabled", "(JI)Z",
+ (void*) nativeIsInputDeviceEnabled },
+ { "nativeEnableInputDevice", "(JI)V",
+ (void*) nativeEnableInputDevice },
+ { "nativeDisableInputDevice", "(JI)V",
+ (void*) nativeDisableInputDevice },
{ "nativeSetPointerIconType", "(JI)V",
(void*) nativeSetPointerIconType },
{ "nativeReloadPointerIcons", "(J)V",
diff --git a/services/core/jni/com_android_server_radio_RadioService.cpp b/services/core/jni/com_android_server_radio_RadioService.cpp
index a6bcdbb..8b53b1b 100644
--- a/services/core/jni/com_android_server_radio_RadioService.cpp
+++ b/services/core/jni/com_android_server_radio_RadioService.cpp
@@ -136,7 +136,7 @@
Region region;
BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
- jobject tuner = env->NewObject(gTunerClass, gTunerCstor, callback, region);
+ jobject tuner = env->NewObject(gTunerClass, gTunerCstor, callback, region, withAudio);
if (tuner == nullptr) {
ALOGE("Unable to create new tuner object.");
return nullptr;
@@ -184,7 +184,7 @@
auto tunerClass = FindClassOrDie(env, "com/android/server/radio/Tuner");
gTunerClass = MakeGlobalRefOrDie(env, tunerClass);
gTunerCstor = GetMethodIDOrDie(env, tunerClass, "<init>",
- "(Landroid/hardware/radio/ITunerCallback;I)V");
+ "(Landroid/hardware/radio/ITunerCallback;IZ)V");
auto serviceClass = FindClassOrDie(env, "com/android/server/radio/RadioService");
gServiceClass = MakeGlobalRefOrDie(env, serviceClass);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c7d6b21..9995a3c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -200,6 +200,8 @@
"com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
private static final String AUTO_FILL_MANAGER_SERVICE_CLASS =
"com.android.server.autofill.AutofillManagerService";
+ private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
+ "com.android.server.timezone.RulesManagerService$Lifecycle";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -1221,6 +1223,13 @@
traceEnd();
}
+ if (!disableNonCoreServices && context.getResources().getBoolean(
+ R.bool.config_enableUpdateableTimeZoneRules)) {
+ traceBeginAndSlog("StartTimeZoneRulesManagerService");
+ mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
traceBeginAndSlog("StartAudioService");
mSystemServiceManager.startService(AudioService.Lifecycle.class);
traceEnd();
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index e2428b9..8ab6d08 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -843,6 +843,36 @@
}
@Test
+ public void testOnUserRemoved() throws Exception {
+ int[] user0Uids = {98, 235, 16, 3782};
+ int[] user1Uids = new int[user0Uids.length];
+ for (int i = 0; i < user0Uids.length; i++) {
+ user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i];
+
+ final ApplicationInfo legacy = new ApplicationInfo();
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
+
+ // create records with the default channel for all user 0 and user 1 uids
+ mHelper.getImportance(PKG, user0Uids[i]);
+ mHelper.getImportance(PKG, user1Uids[i]);
+ }
+
+ mHelper.onUserRemoved(1);
+
+ // user 0 records remain
+ for (int i = 0; i < user0Uids.length; i++) {
+ assertEquals(1,
+ mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
+ }
+ // user 1 records are gone
+ for (int i = 0; i < user1Uids.length; i++) {
+ assertEquals(0,
+ mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
+ }
+ }
+
+ @Test
public void testOnPackageChanged_packageRemoval() throws Exception {
// Deleted
NotificationChannel channel1 =
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index c78488f..bb7e20b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -368,8 +368,8 @@
}
@Test
- public void testSetActiveScorer_noRequestNetworkScoresPermission() {
- when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
+ public void testSetActiveScorer_noScoreNetworksPermission() {
+ when(mContext.checkCallingOrSelfPermission(permission.SCORE_NETWORKS))
.thenReturn(PackageManager.PERMISSION_DENIED);
try {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
index 0694eae..ceb92de 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
@@ -228,30 +228,23 @@
}
@Test
- public void testSetActiveScorer_nullPackage_validDefault() throws Exception {
- String packageName = "package";
- String defaultPackage = "defaultPackage";
- setNetworkRecoPackageSetting(packageName);
- setDefaultNetworkRecommendationPackage(defaultPackage);
- final ComponentName recoComponent = new ComponentName(defaultPackage, "class1");
- mockScoreNetworksGranted(recoComponent.getPackageName());
- mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
+ public void testSetActiveScorer_nullPackage_currentIsSet() throws Exception {
+ setNetworkRecoPackageSetting("package");
assertTrue(mNetworkScorerAppManager.setActiveScorer(null));
verify(mSettingsFacade).putString(mMockContext,
- Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage);
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, null);
+ verify(mSettingsFacade).putInt(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF);
}
@Test
- public void testSetActiveScorer_nullPackage_invalidDefault() throws Exception {
- String packageName = "package";
- String defaultPackage = "defaultPackage";
- setNetworkRecoPackageSetting(packageName);
- setDefaultNetworkRecommendationPackage(defaultPackage);
+ public void testSetActiveScorer_nullPackage_currentIsNull() throws Exception {
+ setNetworkRecoPackageSetting(null);
- assertFalse(mNetworkScorerAppManager.setActiveScorer(null));
- verify(mSettingsFacade, never()).putString(any(),
- eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), any());
+ assertTrue(mNetworkScorerAppManager.setActiveScorer(null));
+ verify(mSettingsFacade, never()).putString(any(), any(), any());
}
@Test
@@ -266,6 +259,9 @@
assertTrue(mNetworkScorerAppManager.setActiveScorer(newPackage));
verify(mSettingsFacade).putString(mMockContext,
Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, newPackage);
+ verify(mSettingsFacade).putInt(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON);
}
@Test
@@ -341,6 +337,32 @@
}
@Test
+ public void testUpdateState_currentPackageNull_defaultNull() throws Exception {
+ setDefaultNetworkRecommendationPackage(null);
+ setNetworkRecoPackageSetting(null);
+
+ mNetworkScorerAppManager.updateState();
+
+ verify(mSettingsFacade, never()).putString(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), anyString());
+ verify(mSettingsFacade, never()).putInt(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt());
+ }
+
+ @Test
+ public void testUpdateState_currentPackageEmpty_defaultEmpty() throws Exception {
+ setDefaultNetworkRecommendationPackage("");
+ setNetworkRecoPackageSetting("");
+
+ mNetworkScorerAppManager.updateState();
+
+ verify(mSettingsFacade, never()).putString(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), anyString());
+ verify(mSettingsFacade, never()).putInt(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt());
+ }
+
+ @Test
public void testUpdateState_currentPackageNotValid_sameAsDefault() throws Exception {
String defaultPackage = "defaultPackage";
setDefaultNetworkRecommendationPackage(defaultPackage);
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 4c53915..27ef9d7 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -56,7 +56,6 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -600,6 +599,39 @@
}
@Test
+ public void selectBackupTransportAsync_calledBeforeInitialize_ignored_nullListener()
+ throws Exception {
+ mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
+ verifyNoMoreInteractions(mBackupManagerServiceMock);
+ // No crash.
+ }
+
+ @Test
+ public void selectBackupTransportAsync_calledBeforeInitialize_ignored_listenerThrowException()
+ throws Exception {
+ mTrampoline.selectBackupTransportAsync(
+ TRANSPORT_COMPONENT_NAME,
+ new ISelectBackupTransportCallback() {
+ @Override
+ public void onSuccess(String transportName) throws RemoteException {
+
+ }
+
+ @Override
+ public void onFailure(int reason) throws RemoteException {
+ throw new RemoteException("Crash");
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ });
+ verifyNoMoreInteractions(mBackupManagerServiceMock);
+ // No crash.
+ }
+
+ @Test
public void selectBackupTransportAsync_forwarded() throws RemoteException {
mTrampoline.initialize(UserHandle.USER_SYSTEM);
mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
new file mode 100644
index 0000000..650681e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.RefactoredBackupManagerService;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AppBackupUtilsTest {
+ private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent";
+ private static final String TEST_PACKAGE_NAME = "test_package";
+
+ private final Random mRandom = new Random(1000000009);
+
+ @Test
+ public void appIsEligibleForBackup_backupNotAllowed_returnsFalse() throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags = 0;
+ applicationInfo.uid = Process.FIRST_APPLICATION_UID;
+ applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
+ applicationInfo.packageName = TEST_PACKAGE_NAME;
+
+ boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
+ public void appIsEligibleForBackup_systemAppWithoutCustomBackupAgent_returnsFalse()
+ throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+ applicationInfo.uid = Process.SYSTEM_UID;
+ applicationInfo.backupAgentName = null;
+ applicationInfo.packageName = TEST_PACKAGE_NAME;
+
+ boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
+ public void appIsEligibleForBackup_sharedStorageBackupPackage_returnsFalse() throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+ applicationInfo.uid = Process.SYSTEM_UID;
+ applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
+ applicationInfo.packageName = RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+
+ boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
+ public void appIsEligibleForBackup_systemAppWithCustomBackupAgent_returnsTrue()
+ throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+ applicationInfo.uid = Process.SYSTEM_UID;
+ applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
+ applicationInfo.packageName = TEST_PACKAGE_NAME;
+
+ boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void appIsEligibleForBackup_nonSystemAppWithoutCustomBackupAgent_returnsTrue()
+ throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+ applicationInfo.uid = Process.FIRST_APPLICATION_UID;
+ applicationInfo.backupAgentName = null;
+ applicationInfo.packageName = TEST_PACKAGE_NAME;
+
+ boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void appIsEligibleForBackup_nonSystemAppWithCustomBackupAgent_returnsTrue()
+ throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+ applicationInfo.uid = Process.FIRST_APPLICATION_UID;
+ applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
+ applicationInfo.packageName = TEST_PACKAGE_NAME;
+
+ boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void appIsStopped_returnsTrue() throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
+
+ boolean isStopped = AppBackupUtils.appIsStopped(applicationInfo);
+
+ assertThat(isStopped).isTrue();
+ }
+
+ @Test
+ public void appIsStopped_returnsFalse() throws Exception {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.flags = ~ApplicationInfo.FLAG_STOPPED;
+
+ boolean isStopped = AppBackupUtils.appIsStopped(applicationInfo);
+
+ assertThat(isStopped).isFalse();
+ }
+
+ @Test
+ public void appGetsFullBackup_noCustomBackupAgent_returnsTrue() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.backupAgentName = null;
+
+ boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void appGetsFullBackup_withCustomBackupAgentAndFullBackupOnlyFlag_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.backupAgentName = "backup.agent";
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+
+ boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void appGetsFullBackup_withCustomBackupAgentAndWithoutFullBackupOnlyFlag_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.backupAgentName = "backup.agent";
+ packageInfo.applicationInfo.flags = ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+
+ boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void appIsKeyValueOnly_noCustomBackupAgent_returnsTrue() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.backupAgentName = null;
+
+ boolean result = AppBackupUtils.appIsKeyValueOnly(packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void appIsKeyValueOnly_withCustomBackupAgentAndFullBackupOnlyFlag_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.backupAgentName = "backup.agent";
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+
+ boolean result = AppBackupUtils.appIsKeyValueOnly(packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void appIsKeyValueOnly_withCustomBackupAgentAndWithoutFullBackupOnlyFlag_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.backupAgentName = "backup.agent";
+ packageInfo.applicationInfo.flags = ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+
+ boolean result = AppBackupUtils.appIsKeyValueOnly(packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], null);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_systemApplication_returnsTrue() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_allowsUnsignedApps_bothSignaturesNull_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = null;
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_allowsUnsignedApps_bothSignaturesEmpty_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[0];
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void
+ signaturesMatch_allowsUnsignedApps_storedSignatureNullTargetSignatureEmpty_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[0];
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void
+ signaturesMatch_allowsUnsignedApps_storedSignatureEmptyTargetSignatureNull_returnsTrue()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = null;
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void
+ signaturesMatch_disallowsAppsUnsignedOnOnlyOneDevice_storedSignatureIsNull_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[]{generateRandomSignature()};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void
+ signaturesMatch_disallowsAppsUnsignedOnOnlyOneDevice_targetSignatureIsNull_returnsFalse()
+ throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = null;
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[]{generateRandomSignature()},
+ packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_signaturesMatch_returnsTrue() throws Exception {
+ Signature signature1 = generateRandomSignature();
+ Signature signature2 = generateRandomSignature();
+ Signature signature3 = generateRandomSignature();
+ assertThat(signature1).isNotEqualTo(signature2);
+ assertThat(signature2).isNotEqualTo(signature3);
+ assertThat(signature1).isNotEqualTo(signature3);
+
+ Signature signature1Copy = new Signature(signature1.toByteArray());
+ Signature signature2Copy = new Signature(signature2.toByteArray());
+ Signature signature3Copy = new Signature(signature3.toByteArray());
+ assertThat(signature1Copy).isEqualTo(signature1);
+ assertThat(signature2Copy).isEqualTo(signature2);
+ assertThat(signature3Copy).isEqualTo(signature3);
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(
+ new Signature[]{signature3Copy, signature1Copy, signature2Copy}, packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
+ Signature signature1 = generateRandomSignature();
+ Signature signature2 = generateRandomSignature();
+ Signature signature3 = generateRandomSignature();
+ assertThat(signature1).isNotEqualTo(signature2);
+ assertThat(signature2).isNotEqualTo(signature3);
+ assertThat(signature1).isNotEqualTo(signature3);
+
+ Signature signature1Copy = new Signature(signature1.toByteArray());
+ Signature signature2Copy = new Signature(signature2.toByteArray());
+ assertThat(signature1Copy).isEqualTo(signature1);
+ assertThat(signature2Copy).isEqualTo(signature2);
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(
+ new Signature[]{signature2Copy, signature1Copy}, packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
+ Signature signature1 = generateRandomSignature();
+ Signature signature2 = generateRandomSignature();
+ Signature signature3 = generateRandomSignature();
+ assertThat(signature1).isNotEqualTo(signature2);
+ assertThat(signature2).isNotEqualTo(signature3);
+ assertThat(signature1).isNotEqualTo(signature3);
+
+ Signature signature1Copy = new Signature(signature1.toByteArray());
+ Signature signature2Copy = new Signature(signature2.toByteArray());
+ assertThat(signature1Copy).isEqualTo(signature1);
+ assertThat(signature2Copy).isEqualTo(signature2);
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[]{signature1Copy, signature2Copy};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(
+ new Signature[]{signature1, signature2, signature3}, packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_emptyStoredSignatures_returnsTrue() throws Exception {
+ Signature signature1 = generateRandomSignature();
+ Signature signature2 = generateRandomSignature();
+ Signature signature3 = generateRandomSignature();
+ assertThat(signature1).isNotEqualTo(signature2);
+ assertThat(signature2).isNotEqualTo(signature3);
+ assertThat(signature1).isNotEqualTo(signature3);
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void signaturesMatch_emptyTargetSignatures_returnsFalse() throws Exception {
+ Signature signature1 = generateRandomSignature();
+ Signature signature2 = generateRandomSignature();
+ Signature signature3 = generateRandomSignature();
+ assertThat(signature1).isNotEqualTo(signature2);
+ assertThat(signature2).isNotEqualTo(signature3);
+ assertThat(signature1).isNotEqualTo(signature3);
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[0];
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(
+ new Signature[]{signature1, signature2, signature3}, packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
+ Signature signature1 = generateRandomSignature();
+ Signature signature2 = generateRandomSignature();
+ Signature signature3 = generateRandomSignature();
+ Signature signature4 = generateRandomSignature();
+ assertThat(signature1).isNotEqualTo(signature2);
+ assertThat(signature2).isNotEqualTo(signature3);
+ assertThat(signature1).isNotEqualTo(signature3);
+ assertThat(signature1).isNotEqualTo(signature4);
+ assertThat(signature2).isNotEqualTo(signature4);
+ assertThat(signature3).isNotEqualTo(signature4);
+
+ Signature signature1Copy = new Signature(signature1.toByteArray());
+ Signature signature2Copy = new Signature(signature2.toByteArray());
+ assertThat(signature1Copy).isEqualTo(signature1);
+ assertThat(signature2Copy).isEqualTo(signature2);
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
+ packageInfo.applicationInfo = new ApplicationInfo();
+
+ boolean result = AppBackupUtils.signaturesMatch(
+ new Signature[]{signature1Copy, signature2Copy, signature4}, packageInfo);
+
+ assertThat(result).isFalse();
+ }
+
+ private Signature generateRandomSignature() {
+ byte[] signatureBytes = new byte[256];
+ mRandom.nextBytes(signatureBytes);
+ return new Signature(signatureBytes);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
new file mode 100644
index 0000000..87c587a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.utils;
+
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_ID;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManagerMonitor;
+import android.content.pm.PackageInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+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;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupManagerMonitorUtilsTest {
+ @Mock private IBackupManagerMonitor mMonitorMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void monitorEvent_monitorIsNull_returnsNull() throws Exception {
+ IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(null, 0, null, 0,
+ null);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void monitorEvent_monitorOnEventThrows_returnsNull() throws Exception {
+ doThrow(new RemoteException()).when(mMonitorMock).onEvent(any(Bundle.class));
+
+ IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 0, null,
+ 0, null);
+
+ verify(mMonitorMock).onEvent(any(Bundle.class));
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void monitorEvent_packageAndExtrasAreNull_fillsBundleCorrectly() throws Exception {
+ IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1, null,
+ 2, null);
+
+ assertThat(result).isEqualTo(mMonitorMock);
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mMonitorMock).onEvent(bundleCaptor.capture());
+ Bundle eventBundle = bundleCaptor.getValue();
+ assertThat(eventBundle.size()).isEqualTo(2);
+ assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1);
+ assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2);
+ }
+
+ @Test
+ public void monitorEvent_packageAndExtrasAreNotNull_fillsBundleCorrectly() throws Exception {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test.package";
+ packageInfo.versionCode = 3;
+ Bundle extras = new Bundle();
+ extras.putInt("key1", 4);
+ extras.putString("key2", "value2");
+
+ IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1,
+ packageInfo, 2, extras);
+
+ assertThat(result).isEqualTo(mMonitorMock);
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mMonitorMock).onEvent(bundleCaptor.capture());
+ Bundle eventBundle = bundleCaptor.getValue();
+ assertThat(eventBundle.size()).isEqualTo(6);
+ assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1);
+ assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2);
+ assertThat(eventBundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME)).isEqualTo("test.package");
+ assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_PACKAGE_VERSION)).isEqualTo(3);
+ assertThat(eventBundle.getInt("key1")).isEqualTo(4);
+ assertThat(eventBundle.getString("key2")).isEqualTo("value2");
+ }
+
+ @Test
+ public void putMonitoringExtraString_bundleExists_fillsBundleCorrectly() throws Exception {
+ Bundle bundle = new Bundle();
+
+ Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(bundle, "key", "value");
+
+ assertThat(result).isEqualTo(bundle);
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getString("key")).isEqualTo("value");
+ }
+
+ @Test
+ public void putMonitoringExtraString_bundleDoesNotExist_fillsBundleCorrectly()
+ throws Exception {
+ Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(null, "key", "value");
+
+ assertThat(result).isNotNull();
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getString("key")).isEqualTo("value");
+ }
+
+
+ @Test
+ public void putMonitoringExtraLong_bundleExists_fillsBundleCorrectly() throws Exception {
+ Bundle bundle = new Bundle();
+
+ Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(bundle, "key", 123);
+
+ assertThat(result).isEqualTo(bundle);
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getLong("key")).isEqualTo(123);
+ }
+
+ @Test
+ public void putMonitoringExtraLong_bundleDoesNotExist_fillsBundleCorrectly() throws Exception {
+ Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(null, "key", 123);
+
+ assertThat(result).isNotNull();
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getLong("key")).isEqualTo(123);
+ }
+
+ @Test
+ public void putMonitoringExtraBoolean_bundleExists_fillsBundleCorrectly() throws Exception {
+ Bundle bundle = new Bundle();
+
+ Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(bundle, "key", true);
+
+ assertThat(result).isEqualTo(bundle);
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getBoolean("key")).isTrue();
+ }
+
+ @Test
+ public void putMonitoringExtraBoolean_bundleDoesNotExist_fillsBundleCorrectly()
+ throws Exception {
+ Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(null, "key", true);
+
+ assertThat(result).isNotNull();
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getBoolean("key")).isTrue();
+ }
+
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
new file mode 100644
index 0000000..ebe6133
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.utils;
+
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.BackupProgress;
+import android.app.backup.IBackupObserver;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupObserverUtilsTest {
+ private static final String PACKAGE_NAME = "some.package";
+
+ @Mock
+ private IBackupObserver mBackupObserverMock;
+ private final BackupProgress mBackupProgress = new BackupProgress(0, 0);
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void sendBackupOnUpdate_observerIsNull_doesNotThrow() throws Exception {
+ BackupObserverUtils.sendBackupOnUpdate(null, PACKAGE_NAME, mBackupProgress);
+
+ // Should not throw.
+ }
+
+ @Test
+ public void sendBackupOnUpdate_callsObserver() throws Exception {
+ BackupObserverUtils.sendBackupOnUpdate(mBackupObserverMock, PACKAGE_NAME, mBackupProgress);
+
+ verify(mBackupObserverMock).onUpdate(PACKAGE_NAME, mBackupProgress);
+ }
+
+ @Test
+ public void sendBackupOnUpdate_handlesRemoteException() throws Exception {
+ doThrow(new RemoteException()).when(mBackupObserverMock).onUpdate(PACKAGE_NAME,
+ mBackupProgress);
+
+ BackupObserverUtils.sendBackupOnUpdate(mBackupObserverMock, PACKAGE_NAME, mBackupProgress);
+
+ verify(mBackupObserverMock).onUpdate(PACKAGE_NAME, mBackupProgress);
+ }
+
+ @Test
+ public void sendBackupOnPackageResult_observerIsNull_doesNotThrow() throws Exception {
+ BackupObserverUtils.sendBackupOnPackageResult(null, PACKAGE_NAME, 1);
+
+ // Should not throw.
+ }
+
+ @Test
+ public void sendBackupOnPackageResult_callsObserver() throws Exception {
+ BackupObserverUtils.sendBackupOnPackageResult(mBackupObserverMock, PACKAGE_NAME, 1);
+
+ verify(mBackupObserverMock).onResult(PACKAGE_NAME, 1);
+ }
+
+ @Test
+ public void sendBackupOnPackageResult_handlesRemoteException() throws Exception {
+ doThrow(new RemoteException()).when(mBackupObserverMock).onResult(PACKAGE_NAME, 1);
+
+ BackupObserverUtils.sendBackupOnPackageResult(mBackupObserverMock, PACKAGE_NAME, 1);
+
+ verify(mBackupObserverMock).onResult(PACKAGE_NAME, 1);
+ }
+
+ @Test
+ public void sendBackupFinished_observerIsNull_doesNotThrow() throws Exception {
+ BackupObserverUtils.sendBackupFinished(null, 1);
+
+ // Should not throw.
+ }
+
+ @Test
+ public void sendBackupFinished_callsObserver() throws Exception {
+ BackupObserverUtils.sendBackupFinished(mBackupObserverMock, 1);
+
+ verify(mBackupObserverMock).backupFinished(1);
+ }
+
+ @Test
+ public void sendBackupFinished_handlesRemoteException() throws Exception {
+ doThrow(new RemoteException()).when(mBackupObserverMock).onResult(PACKAGE_NAME, 1);
+
+ BackupObserverUtils.sendBackupFinished(mBackupObserverMock, 1);
+
+ verify(mBackupObserverMock).backupFinished(1);
+ }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
new file mode 100644
index 0000000..2f56598
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IFullBackupRestoreObserver;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class FullBackupRestoreObserverUtilsTest {
+ private static final String PACKAGE_NAME = "some.package";
+ @Mock
+ private IFullBackupRestoreObserver mFullBackupRestoreObserverMock;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void sendStartRestore_observerIsNull_returnsNull() throws Exception {
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendStartRestore(null);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void sendStartRestore_callsObserver() throws Exception {
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendStartRestore(
+ mFullBackupRestoreObserverMock);
+
+ assertThat(result).isEqualTo(mFullBackupRestoreObserverMock);
+ verify(mFullBackupRestoreObserverMock).onStartRestore();
+ }
+
+ @Test
+ public void sendStartRestore_observerThrows_returnsNull() throws Exception {
+ doThrow(new RemoteException()).when(mFullBackupRestoreObserverMock).onStartRestore();
+
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendStartRestore(
+ mFullBackupRestoreObserverMock);
+
+ assertThat(result).isNull();
+ verify(mFullBackupRestoreObserverMock).onStartRestore();
+ }
+
+ @Test
+ public void sendOnRestorePackage_observerIsNull_returnsNull() throws Exception {
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendOnRestorePackage(
+ null, PACKAGE_NAME);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void sendOnRestorePackage_callsObserver() throws Exception {
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendOnRestorePackage(
+ mFullBackupRestoreObserverMock, PACKAGE_NAME);
+
+ assertThat(result).isEqualTo(mFullBackupRestoreObserverMock);
+ verify(mFullBackupRestoreObserverMock).onRestorePackage(PACKAGE_NAME);
+ }
+
+ @Test
+ public void sendOnRestorePackage_observerThrows_returnsNull() throws Exception {
+ doThrow(new RemoteException()).when(mFullBackupRestoreObserverMock).onRestorePackage(
+ PACKAGE_NAME);
+
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendOnRestorePackage(
+ mFullBackupRestoreObserverMock, PACKAGE_NAME);
+
+ assertThat(result).isNull();
+ verify(mFullBackupRestoreObserverMock).onRestorePackage(PACKAGE_NAME);
+ }
+
+ @Test
+ public void sendEndRestore_observerIsNull_returnsNull() throws Exception {
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendEndRestore(null);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void sendEndRestore_callsObserver() throws Exception {
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendEndRestore(
+ mFullBackupRestoreObserverMock);
+
+ assertThat(result).isEqualTo(mFullBackupRestoreObserverMock);
+ verify(mFullBackupRestoreObserverMock).onEndRestore();
+ }
+
+ @Test
+ public void sendEndRestore_observerThrows_returnsNull() throws Exception {
+ doThrow(new RemoteException()).when(mFullBackupRestoreObserverMock).onEndRestore();
+
+ IFullBackupRestoreObserver result = FullBackupRestoreObserverUtils.sendEndRestore(
+ mFullBackupRestoreObserverMock);
+
+ assertThat(result).isNull();
+ verify(mFullBackupRestoreObserverMock).onEndRestore();
+ }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 325d99a..5e4ba7be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -31,13 +31,10 @@
import static org.junit.Assert.fail;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.UserInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.support.test.InstrumentationRegistry;
@@ -49,26 +46,17 @@
import android.util.LongSparseArray;
import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastPrintWriter;
import com.android.server.LocalServices;
-import org.hamcrest.core.IsNot;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.PrintStream;
import java.security.PublicKey;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -547,7 +535,6 @@
private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) {
- assertThat(userState.blockUninstall, is(false));
assertThat(userState.enabled, is(0));
assertThat(userState.hidden, is(false));
assertThat(userState.installed, is(installed));
@@ -783,31 +770,14 @@
@Before
public void createUserManagerServiceRef() throws ReflectiveOperationException {
- InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- Constructor<UserManagerService> umsc;
- try {
- // unregister the user manager from the local service
- Method removeServiceForTest = LocalServices.class.getDeclaredMethod(
- "removeServiceForTest", Class.class);
- removeServiceForTest.invoke(null, UserManagerInternal.class);
-
- // now create a new user manager [which registers again with the local service]
- umsc = UserManagerService.class.getDeclaredConstructor(
- Context.class,
- PackageManagerService.class,
- Object.class,
- File.class);
- umsc.setAccessible(true);
- UserManagerService ums = umsc.newInstance(InstrumentationRegistry.getContext(),
- null /*PackageManagerService*/, new Object() /*packagesLock*/,
- new File(InstrumentationRegistry.getContext().getFilesDir(), "user"));
- } catch (SecurityException
- | ReflectiveOperationException
- | IllegalArgumentException e) {
- fail("Could not create user manager service; " + e);
- }
+ InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
+ try {
+ // unregister the user manager from the local service
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ new UserManagerService(InstrumentationRegistry.getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Could not create user manager service; " + e);
}
});
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index bf05f21..50be8db 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -49,10 +49,6 @@
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserState();
- oldUserState.blockUninstall = true;
- assertThat(testUserState.equals(oldUserState), is(false));
-
- oldUserState = new PackageUserState();
oldUserState.ceDataInode = 4000L;
assertThat(testUserState.equals(oldUserState), is(false));
diff --git a/services/tests/servicestests/src/com/android/server/timezone/CheckTokenTest.java b/services/tests/servicestests/src/com/android/server/timezone/CheckTokenTest.java
new file mode 100644
index 0000000..9603a06
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezone/CheckTokenTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import org.junit.Test;
+
+import android.support.test.filters.SmallTest;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+@SmallTest
+public class CheckTokenTest {
+
+ @Test
+ public void toByteArray() throws Exception {
+ PackageVersions packageVersions =
+ new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */);
+ CheckToken originalToken = new CheckToken(1 /* optimisticLockId */, packageVersions);
+ assertEquals(originalToken, CheckToken.fromByteArray(originalToken.toByteArray()));
+ }
+
+ @Test
+ public void fromByteArray() {
+ PackageVersions packageVersions =
+ new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */);
+ CheckToken token = new CheckToken(1, packageVersions);
+ byte[] validTokenBytes = token.toByteArray();
+ byte[] shortTokenBytes = new byte[validTokenBytes.length - 1];
+ System.arraycopy(validTokenBytes, 0, shortTokenBytes, 0, shortTokenBytes.length);
+
+ try {
+ CheckToken.fromByteArray(shortTokenBytes);
+ fail();
+ } catch (IOException expected) {}
+ }
+
+ @Test
+ public void equals() {
+ PackageVersions packageVersions1 =
+ new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */);
+ PackageVersions packageVersions2 =
+ new PackageVersions(2 /* updateAppVersion */, 2 /* dataAppVersion */);
+ assertFalse(packageVersions1.equals(packageVersions2));
+
+ CheckToken baseline = new CheckToken(1, packageVersions1);
+ assertEquals(baseline, baseline);
+
+ CheckToken deepEqual = new CheckToken(1, packageVersions1);
+ assertEquals(baseline, deepEqual);
+
+ CheckToken differentOptimisticLockId = new CheckToken(2, packageVersions1);
+ assertFalse(differentOptimisticLockId.equals(baseline));
+
+ CheckToken differentPackageVersions = new CheckToken(1, packageVersions2);
+ assertFalse(differentPackageVersions.equals(baseline));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
new file mode 100644
index 0000000..e085270
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+@SmallTest
+public class PackageStatusStorageTest {
+ private static final PackageVersions VALID_PACKAGE_VERSIONS = new PackageVersions(1, 2);
+
+ private PackageStatusStorage mPackageStatusStorage;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+
+ // Using the instrumentation context means the database is created in a test app-specific
+ // directory.
+ mPackageStatusStorage = new PackageStatusStorage(context);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mPackageStatusStorage.deleteDatabaseForTests();
+ }
+
+ @Test
+ public void getPackageStatus_initialState() {
+ assertNull(mPackageStatusStorage.getPackageStatus());
+ }
+
+ @Test
+ public void resetCheckState() {
+ // Assert initial state.
+ assertNull(mPackageStatusStorage.getPackageStatus());
+
+ CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+
+ // There should now be a state.
+ assertNotNull(mPackageStatusStorage.getPackageStatus());
+
+ // Now clear the state.
+ mPackageStatusStorage.resetCheckState();
+
+ // After reset, there should be no package state again.
+ assertNull(mPackageStatusStorage.getPackageStatus());
+
+ CheckToken token2 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+
+ // Token after a reset should still be distinct.
+ assertFalse(token1.equals(token2));
+
+ // Now clear the state again.
+ mPackageStatusStorage.resetCheckState();
+
+ // After reset, there should be no package state again.
+ assertNull(mPackageStatusStorage.getPackageStatus());
+
+ CheckToken token3 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+
+ // A CheckToken generated after a reset should still be distinct.
+ assertFalse(token2.equals(token3));
+ }
+
+ @Test
+ public void generateCheckToken_missingRowBehavior() {
+ // Assert initial state.
+ assertNull(mPackageStatusStorage.getPackageStatus());
+
+ CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+ assertNotNull(token1);
+
+ // There should now be state.
+ assertNotNull(mPackageStatusStorage.getPackageStatus());
+
+ // Corrupt the table by removing the one row.
+ mPackageStatusStorage.deleteRowForTests();
+
+ // Check that generateCheckToken recovers.
+ assertNotNull(mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS));
+ }
+
+ @Test
+ public void getPackageStatus_missingRowBehavior() {
+ // Assert initial state.
+ assertNull(mPackageStatusStorage.getPackageStatus());
+
+ CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+ assertNotNull(token1);
+
+ // There should now be a state.
+ assertNotNull(mPackageStatusStorage.getPackageStatus());
+
+ // Corrupt the table by removing the one row.
+ mPackageStatusStorage.deleteRowForTests();
+
+ assertNull(mPackageStatusStorage.getPackageStatus());
+ }
+
+ @Test
+ public void markChecked_missingRowBehavior() {
+ // Assert initial state.
+ CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+ assertNotNull(token1);
+
+ // There should now be a state.
+ assertNotNull(mPackageStatusStorage.getPackageStatus());
+
+ // Corrupt the table by removing the one row.
+ mPackageStatusStorage.deleteRowForTests();
+
+ // The missing row should mean token1 is now considered invalid, so we should get a false.
+ assertFalse(mPackageStatusStorage.markChecked(token1, true /* succeeded */));
+
+ // The storage should have recovered and we should be able to carry on like before.
+ CheckToken token2 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+ assertTrue(mPackageStatusStorage.markChecked(token2, true /* succeeded */));
+ }
+
+ @Test
+ public void checkToken_tokenIsUnique() {
+ PackageVersions packageVersions = VALID_PACKAGE_VERSIONS;
+ PackageStatus expectedPackageStatus =
+ new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions);
+
+ CheckToken token1 = mPackageStatusStorage.generateCheckToken(packageVersions);
+ assertEquals(packageVersions, token1.mPackageVersions);
+
+ PackageStatus actualPackageStatus1 = mPackageStatusStorage.getPackageStatus();
+ assertEquals(expectedPackageStatus, actualPackageStatus1);
+
+ CheckToken token2 = mPackageStatusStorage.generateCheckToken(packageVersions);
+ assertEquals(packageVersions, token1.mPackageVersions);
+ assertFalse(token1.mOptimisticLockId == token2.mOptimisticLockId);
+ assertFalse(token1.equals(token2));
+ }
+
+ @Test
+ public void markChecked_checkSucceeded() {
+ PackageVersions packageVersions = VALID_PACKAGE_VERSIONS;
+
+ CheckToken token = mPackageStatusStorage.generateCheckToken(packageVersions);
+ boolean writeOk = mPackageStatusStorage.markChecked(token, true /* succeeded */);
+ assertTrue(writeOk);
+
+ PackageStatus expectedPackageStatus =
+ new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
+ assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus());
+ }
+
+ @Test
+ public void markChecked_checkFailed() {
+ PackageVersions packageVersions = VALID_PACKAGE_VERSIONS;
+
+ CheckToken token = mPackageStatusStorage.generateCheckToken(packageVersions);
+ boolean writeOk = mPackageStatusStorage.markChecked(token, false /* succeeded */);
+ assertTrue(writeOk);
+
+ PackageStatus expectedPackageStatus =
+ new PackageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, packageVersions);
+ assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus());
+ }
+
+ @Test
+ public void markChecked_optimisticLocking_multipleToken() {
+ PackageVersions packageVersions = VALID_PACKAGE_VERSIONS;
+ CheckToken token1 = mPackageStatusStorage.generateCheckToken(packageVersions);
+ CheckToken token2 = mPackageStatusStorage.generateCheckToken(packageVersions);
+
+ PackageStatus packageStatusBeforeChecked = mPackageStatusStorage.getPackageStatus();
+
+ boolean writeOk1 = mPackageStatusStorage.markChecked(token1, true /* succeeded */);
+ // Generation of token2 should mean that token1 is no longer valid.
+ assertFalse(writeOk1);
+ assertEquals(packageStatusBeforeChecked, mPackageStatusStorage.getPackageStatus());
+
+ boolean writeOk2 = mPackageStatusStorage.markChecked(token2, true /* succeeded */);
+ // token2 should still be valid, and the attempt with token1 should have had no effect.
+ assertTrue(writeOk2);
+ PackageStatus expectedPackageStatus =
+ new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
+ assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus());
+ }
+
+ @Test
+ public void markChecked_optimisticLocking_repeatedTokenUse() {
+ PackageVersions packageVersions = VALID_PACKAGE_VERSIONS;
+ CheckToken token = mPackageStatusStorage.generateCheckToken(packageVersions);
+
+ boolean writeOk1 = mPackageStatusStorage.markChecked(token, true /* succeeded */);
+ assertTrue(writeOk1);
+
+ PackageStatus expectedPackageStatus =
+ new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
+ assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus());
+
+ // token cannot be reused.
+ boolean writeOk2 = mPackageStatusStorage.markChecked(token, true /* succeeded */);
+ assertFalse(writeOk2);
+ assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusTest.java
new file mode 100644
index 0000000..c0ae81e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import org.junit.Test;
+
+import android.support.test.filters.SmallTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+@SmallTest
+public class PackageStatusTest {
+
+ @Test
+ public void equals() {
+ PackageVersions packageVersions1 =
+ new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */);
+ PackageVersions packageVersions2 =
+ new PackageVersions(2 /* updateAppVersion */, 1 /* dataAppVersion */);
+ assertFalse(packageVersions1.equals(packageVersions2));
+
+ PackageStatus baseline =
+ new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions1);
+ assertEquals(baseline, baseline);
+
+ PackageStatus deepEqual =
+ new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions1);
+ assertEquals(baseline, deepEqual);
+
+ PackageStatus differentStatus =
+ new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions1);
+ assertFalse(differentStatus.equals(baseline));
+
+ PackageStatus differentPackageVersions =
+ new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions2);
+ assertFalse(differentPackageVersions.equals(baseline));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
new file mode 100644
index 0000000..45b0af3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -0,0 +1,1471 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import android.app.timezone.RulesUpdaterContract;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.TimeZoneRulesDataContract;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+
+/**
+ * White box interaction / unit testing of the {@link PackageTracker}.
+ */
+@SmallTest
+public class PackageTrackerTest {
+ private static final String UPDATE_APP_PACKAGE_NAME = "updateAppPackageName";
+ private static final String DATA_APP_PACKAGE_NAME = "dataAppPackageName";
+ private static final PackageVersions INITIAL_APP_PACKAGE_VERSIONS =
+ new PackageVersions(2 /* updateAppVersion */, 2 /* dataAppVersion */);
+
+ private ConfigHelper mMockConfigHelper;
+ private PackageManagerHelper mMockPackageManagerHelper;
+
+ private FakeClockHelper mFakeClock;
+ private FakeIntentHelper mFakeIntentHelper;
+ private PackageStatusStorage mPackageStatusStorage;
+ private PackageTracker mPackageTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+
+ mFakeClock = new FakeClockHelper();
+
+ // Read-only interfaces so are easy to mock.
+ mMockConfigHelper = mock(ConfigHelper.class);
+ mMockPackageManagerHelper = mock(PackageManagerHelper.class);
+
+ // Using the instrumentation context means the database is created in a test app-specific
+ // directory. We can use the real thing for this test.
+ mPackageStatusStorage = new PackageStatusStorage(context);
+
+ // For other interactions with the Android framework we create a fake object.
+ mFakeIntentHelper = new FakeIntentHelper();
+
+ // Create the PackageTracker to use in tests.
+ mPackageTracker = new PackageTracker(
+ mFakeClock,
+ mMockConfigHelper,
+ mMockPackageManagerHelper,
+ mPackageStatusStorage,
+ mFakeIntentHelper);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mPackageStatusStorage != null) {
+ mPackageStatusStorage.deleteDatabaseForTests();
+ }
+ }
+
+ @Test
+ public void trackingDisabled_intentHelperNotUsed() {
+ // Set up device configuration.
+ configureTrackingDisabled();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the IntentHelper was not initialized.
+ mFakeIntentHelper.assertNotInitialized();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ }
+
+ @Test
+ public void trackingDisabled_triggerUpdateIfNeededNotAllowed() {
+ // Set up device configuration.
+ configureTrackingDisabled();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ try {
+ // This call should also not be allowed and will throw an exception if tracking is
+ // disabled.
+ mPackageTracker.triggerUpdateIfNeeded(true);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ }
+
+ @Test
+ public void trackingDisabled_unsolicitedResultsIgnored_withoutToken() {
+ // Set up device configuration.
+ configureTrackingDisabled();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ // Receiving a check result when tracking is disabled should cause the storage to be
+ // reset.
+ mPackageTracker.recordCheckResult(null /* checkToken */, true /* success */);
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ // Assert the storage was reset.
+ checkPackageStorageStatusIsInitialOrReset();
+ }
+
+ @Test
+ public void trackingDisabled_unsolicitedResultsIgnored_withToken() {
+ // Set up device configuration.
+ configureTrackingDisabled();
+
+ // Set the storage into an arbitrary state so we can detect a reset.
+ mPackageStatusStorage.generateCheckToken(INITIAL_APP_PACKAGE_VERSIONS);
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ // Receiving a check result when tracking is disabled should cause the storage to be reset.
+ mPackageTracker.recordCheckResult(createArbitraryCheckToken(), true /* success */);
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ // Assert the storage was reset.
+ checkPackageStorageStatusIsInitialOrReset();
+ }
+
+ @Test
+ public void trackingEnabled_updateAppConfigMissing() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureUpdateAppPackageNameMissing();
+ configureDataAppPackageOk(DATA_APP_PACKAGE_NAME);
+
+ try {
+ // Initialize the tracker.
+ mPackageTracker.start();
+ fail();
+ } catch (RuntimeException expected) {}
+
+ mFakeIntentHelper.assertNotInitialized();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ }
+
+ // TODO(nfuller): Uncomment or delete when it's clear what will happen with http://b/35995024
+ // @Test
+ // public void trackingEnabled_updateAppNotPrivileged() throws Exception {
+ // // Set up device configuration.
+ // configureTrackingEnabled();
+ // configureReliabilityConfigSettingsOk();
+ // configureUpdateAppPackageNotPrivileged(UPDATE_APP_PACKAGE_NAME);
+ // configureDataAppPackageOk(DATA_APP_PACKAGE_NAME);
+ //
+ // try {
+ // // Initialize the tracker.
+ // mPackageTracker.start();
+ // fail();
+ // } catch (RuntimeException expected) {}
+ //
+ // mFakeIntentHelper.assertNotInitialized();
+ //
+ // // Check reliability triggering state.
+ // mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ // }
+
+ @Test
+ public void trackingEnabled_dataAppConfigMissing() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME);
+ configureDataAppPackageNameMissing();
+
+ try {
+ // Initialize the tracker.
+ mPackageTracker.start();
+ fail();
+ } catch (RuntimeException expected) {}
+
+ mFakeIntentHelper.assertNotInitialized();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ }
+
+ // TODO(nfuller): Uncomment or delete when it's clear what will happen with http://b/35995024
+ // @Test
+ // public void trackingEnabled_dataAppNotPrivileged() throws Exception {
+ // // Set up device configuration.
+ // configureTrackingEnabled();
+ // configureReliabilityConfigSettingsOk();
+ // configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME);
+ // configureDataAppPackageNotPrivileged(DATA_APP_PACKAGE_NAME);
+ //
+ // try {
+ // // Initialize the tracker.
+ // mPackageTracker.start();
+ // fail();
+ // } catch (RuntimeException expected) {}
+ //
+ // mFakeIntentHelper.assertNotInitialized();
+ //
+ // // Check reliability triggering state.
+ // mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ // }
+
+ @Test
+ public void trackingEnabled_packageUpdate_badUpdateAppManifestEntry() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Configure a bad manifest for the update app. Should effectively turn off tracking.
+ PackageVersions packageVersions =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ configureUpdateAppManifestBad(UPDATE_APP_PACKAGE_NAME);
+ configureDataAppManifestOk(DATA_APP_PACKAGE_NAME);
+ configureUpdateAppPackageVersion(
+ UPDATE_APP_PACKAGE_NAME, packageVersions.mUpdateAppVersion);
+ configureDataAppPackageVersion(DATA_APP_PACKAGE_NAME, packageVersions.mDataAppVersion);
+ // Simulate a tracked package being updated.
+ mFakeIntentHelper.simulatePackageUpdatedEvent();
+
+ // Assert the PackageTracker did not attempt to trigger an update.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ // Assert the storage was not touched.
+ checkPackageStorageStatusIsInitialOrReset();
+ }
+
+ @Test
+ public void trackingEnabled_packageUpdate_badDataAppManifestEntry() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Configure a bad manifest for the data app. Should effectively turn off tracking.
+ PackageVersions packageVersions =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ configureUpdateAppManifestOk(UPDATE_APP_PACKAGE_NAME);
+ configureDataAppManifestBad(DATA_APP_PACKAGE_NAME);
+ configureUpdateAppPackageVersion(
+ UPDATE_APP_PACKAGE_NAME, packageVersions.mUpdateAppVersion);
+ configureDataAppPackageVersion(DATA_APP_PACKAGE_NAME, packageVersions.mDataAppVersion);
+ mFakeIntentHelper.simulatePackageUpdatedEvent();
+
+ // Assert the PackageTracker did not attempt to trigger an update.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ // Assert the storage was not touched.
+ checkPackageStorageStatusIsInitialOrReset();
+ }
+
+ @Test
+ public void trackingEnabled_packageUpdate_responseWithToken_success() throws Exception {
+ trackingEnabled_packageUpdate_responseWithToken(true);
+ }
+
+ @Test
+ public void trackingEnabled_packageUpdate_responseWithToken_failed() throws Exception {
+ trackingEnabled_packageUpdate_responseWithToken(false);
+ }
+
+ private void trackingEnabled_packageUpdate_responseWithToken(boolean success) throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate a tracked package being updated.
+ PackageVersions packageVersions =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions);
+
+ // Get the token that was passed to the intent helper, and pass it back.
+ CheckToken token = mFakeIntentHelper.captureAndResetLastToken();
+ mPackageTracker.recordCheckResult(token, success);
+
+ // Check storage and reliability triggering state.
+ if (success) {
+ checkUpdateCheckSuccessful(packageVersions);
+ } else {
+ checkUpdateCheckFailed(packageVersions);
+ }
+ }
+
+ @Test
+ public void trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset_success()
+ throws Exception {
+ trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset(true);
+ }
+
+ @Test
+ public void trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset_failed()
+ throws Exception {
+ trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset(false);
+ }
+
+ private void trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset(
+ boolean success) throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Set up installed app versions / manifests.
+ PackageVersions packageVersions =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions);
+
+ // Ignore the token that was given to the intent helper, just pass null.
+ mPackageTracker.recordCheckResult(null /* checkToken */, success);
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+
+ // Assert the storage was reset.
+ checkPackageStorageStatusIsInitialOrReset();
+ }
+
+ /**
+ * Two package updates triggered for the same package versions. The second is triggered while
+ * the first is still happening.
+ */
+ @Test
+ public void trackingEnabled_packageUpdate_twoChecksNoPackageChange_secondWhileFirstInProgress()
+ throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate package installation.
+ PackageVersions packageVersions =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions);
+
+ // Get the first token.
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions, token1.mPackageVersions);
+
+ // Now attempt to generate another check while the first is in progress and without having
+ // updated the package versions. The PackageTracker should trigger again for safety.
+ simulatePackageInstallation(packageVersions);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions);
+
+ CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions, token2.mPackageVersions);
+ assertEquals(token1.mPackageVersions, token2.mPackageVersions);
+ assertTrue(token1.mOptimisticLockId != token2.mOptimisticLockId);
+ }
+
+ /**
+ * Two package updates triggered for the same package versions. The second happens after
+ * the first has succeeded.
+ */
+ @Test
+ public void trackingEnabled_packageUpdate_twoChecksNoPackageChange_sequential()
+ throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate package installation.
+ PackageVersions packageVersions =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions);
+
+ // Get the token.
+ CheckToken token = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions, token.mPackageVersions);
+
+ // Simulate a successful check.
+ mPackageTracker.recordCheckResult(token, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions);
+
+ // Now attempt to generate another check, but without having updated the package. The
+ // PackageTracker should be smart enough to recognize there's nothing to do here.
+ simulatePackageInstallation(packageVersions);
+
+ // Assert the PackageTracker did not attempt to trigger an update.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions);
+ }
+
+ /**
+ * Two package updates triggered for the same package versions. The second is triggered after
+ * the first has failed.
+ */
+ @Test
+ public void trackingEnabled_packageUpdate_afterFailure() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate package installation.
+ PackageVersions packageVersions =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions);
+
+ // Get the first token.
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions, token1.mPackageVersions);
+
+ // Simulate an *unsuccessful* check.
+ mPackageTracker.recordCheckResult(token1, false /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckFailed(packageVersions);
+
+ // Now generate another check, but without having updated the package. The
+ // PackageTracker should recognize the last check failed and trigger again.
+ simulatePackageInstallation(packageVersions);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions);
+
+ // Get the second token.
+ CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken();
+
+ // Assert some things about the tokens.
+ assertEquals(packageVersions, token2.mPackageVersions);
+ assertTrue(token1.mOptimisticLockId != token2.mOptimisticLockId);
+
+ // For completeness, now simulate this check was successful.
+ mPackageTracker.recordCheckResult(token2, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions);
+ }
+
+ /**
+ * Two package updates triggered for different package versions. The second is triggered while
+ * the first is still happening.
+ */
+ @Test
+ public void trackingEnabled_packageUpdate_twoChecksWithPackageChange_firstCheckInProcess()
+ throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate package installation.
+ PackageVersions packageVersions1 =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions1);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions1);
+
+ // Get the first token.
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions1, token1.mPackageVersions);
+
+ // Simulate a tracked package being updated a second time (before the response for the
+ // first has been received).
+ PackageVersions packageVersions2 =
+ new PackageVersions(3 /* updateAppPackageVersion */, 4 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions2);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions2);
+
+ // Get the second token.
+ CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions2, token2.mPackageVersions);
+
+ // token1 should be invalid because the token2 was generated.
+ mPackageTracker.recordCheckResult(token1, true /* success */);
+
+ // Reliability triggering should still be enabled.
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+
+ // Check the expected storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_STARTED, packageVersions2);
+
+ // token2 should still be accepted.
+ mPackageTracker.recordCheckResult(token2, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions2);
+ }
+
+ /**
+ * Two package updates triggered for different package versions. The second is triggered after
+ * the first has completed successfully.
+ */
+ @Test
+ public void trackingEnabled_packageUpdate_twoChecksWithPackageChange_sequential()
+ throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate package installation.
+ PackageVersions packageVersions1 =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions1);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions1);
+
+ // Get the first token.
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions1, token1.mPackageVersions);
+
+ // token1 should be accepted.
+ mPackageTracker.recordCheckResult(token1, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions1);
+
+ // Simulate a tracked package being updated a second time.
+ PackageVersions packageVersions2 =
+ new PackageVersions(3 /* updateAppPackageVersion */, 4 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions2);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions2);
+
+ // Get the second token.
+ CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions2, token2.mPackageVersions);
+
+ // token2 should still be accepted.
+ mPackageTracker.recordCheckResult(token2, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions2);
+ }
+
+ /**
+ * Replaying the same token twice.
+ */
+ @Test
+ public void trackingEnabled_packageUpdate_sameTokenReplayFails() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate package installation.
+ PackageVersions packageVersions1 =
+ new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */);
+ simulatePackageInstallation(packageVersions1);
+
+ // Confirm an update was triggered.
+ checkUpdateCheckTriggered(packageVersions1);
+
+ // Get the first token.
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions1, token1.mPackageVersions);
+
+ // token1 should be accepted.
+ mPackageTracker.recordCheckResult(token1, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions1);
+
+ // Apply token1 again.
+ mPackageTracker.recordCheckResult(token1, true /* success */);
+
+ // Check the expected storage state. No real way to tell if it has been updated, but
+ // we can check the final state is still what it should be.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions1);
+
+ // Under the covers we expect it to fail to update because the storage should recognize that
+ // the token is no longer valid.
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+
+ // Peek inside the package tracker to make sure it is tracking failure counts properly.
+ assertEquals(1, mPackageTracker.getCheckFailureCountForTests());
+ }
+
+ @Test
+ public void trackingEnabled_reliabilityTrigger_firstTime_initialStorage() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ PackageVersions packageVersions = configureValidApplications();
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatusIsInitialOrReset();
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(packageVersions);
+
+ // Confirm the token was correct.
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+ assertEquals(packageVersions, token1.mPackageVersions);
+
+ // token1 should be accepted.
+ mPackageTracker.recordCheckResult(token1, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions);
+ }
+
+ @Test
+ public void trackingEnabled_reliabilityTrigger_afterRebootNoTriggerNeeded() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ PackageVersions packageVersions = configureValidApplications();
+
+ // Force the storage into a state we want.
+ mPackageStatusStorage.forceCheckStateForTests(
+ PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did not attempt to trigger an update.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(packageVersions);
+ }
+
+ /**
+ * Simulates the device starting where the storage records do not match the installed app
+ * versions. The reliability trigger should cause the package tracker to perform a check.
+ */
+ @Test
+ public void trackingEnabled_reliabilityTrigger_afterRebootTriggerNeededBecausePreviousFailed()
+ throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+
+ PackageVersions oldPackageVersions = new PackageVersions(1, 1);
+ PackageVersions currentPackageVersions = new PackageVersions(2, 2);
+
+ // Simulate there being a newer version installed than the one recorded in storage.
+ configureValidApplications(currentPackageVersions);
+
+ // Force the storage into a state we want.
+ mPackageStatusStorage.forceCheckStateForTests(
+ PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(currentPackageVersions);
+
+ // Simulate the update check completing successfully.
+ CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken();
+ mPackageTracker.recordCheckResult(checkToken, true /* success */);
+
+ // Check storage and reliability triggering state.
+ checkUpdateCheckSuccessful(currentPackageVersions);
+ }
+
+ /**
+ * Simulates persistent failures of the reliability check. It should stop after the configured
+ * number of checks.
+ */
+ @Test
+ public void trackingEnabled_reliabilityTrigger_repeatedFailures() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+
+ int retriesAllowed = 3;
+ int checkDelayMillis = 5 * 60 * 1000;
+ configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis);
+
+ PackageVersions oldPackageVersions = new PackageVersions(1, 1);
+ PackageVersions currentPackageVersions = new PackageVersions(2, 2);
+
+ // Simulate there being a newer version installed than the one recorded in storage.
+ configureValidApplications(currentPackageVersions);
+
+ // Force the storage into a state we want.
+ mPackageStatusStorage.forceCheckStateForTests(
+ PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ for (int i = 0; i < retriesAllowed + 1; i++) {
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(currentPackageVersions);
+
+ // Check the PackageTracker failure count before calling recordCheckResult.
+ assertEquals(i, mPackageTracker.getCheckFailureCountForTests());
+
+ // Simulate a check failure.
+ CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken();
+ mPackageTracker.recordCheckResult(checkToken, false /* success */);
+
+ // Peek inside the package tracker to make sure it is tracking failure counts properly.
+ assertEquals(i + 1, mPackageTracker.getCheckFailureCountForTests());
+
+ // Confirm nothing has changed.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE,
+ currentPackageVersions);
+
+ // Check reliability triggering is in the correct state.
+ if (i <= retriesAllowed) {
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ } else {
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ }
+ }
+ }
+
+ @Test
+ public void trackingEnabled_reliabilityTrigger_failureCountIsReset() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+
+ int retriesAllowed = 3;
+ int checkDelayMillis = 5 * 60 * 1000;
+ configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis);
+
+ PackageVersions oldPackageVersions = new PackageVersions(1, 1);
+ PackageVersions currentPackageVersions = new PackageVersions(2, 2);
+
+ // Simulate there being a newer version installed than the one recorded in storage.
+ configureValidApplications(currentPackageVersions);
+
+ // Force the storage into a state we want.
+ mPackageStatusStorage.forceCheckStateForTests(
+ PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ // Fail (retries - 1) times.
+ for (int i = 0; i < retriesAllowed - 1; i++) {
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(currentPackageVersions);
+
+ // Check the PackageTracker failure count before calling recordCheckResult.
+ assertEquals(i, mPackageTracker.getCheckFailureCountForTests());
+
+ // Simulate a check failure.
+ CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken();
+ mPackageTracker.recordCheckResult(checkToken, false /* success */);
+
+ // Peek inside the package tracker to make sure it is tracking failure counts properly.
+ assertEquals(i + 1, mPackageTracker.getCheckFailureCountForTests());
+
+ // Confirm nothing has changed.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE,
+ currentPackageVersions);
+
+ // Check reliability triggering is still enabled.
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ }
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(currentPackageVersions);
+
+ // Check the PackageTracker failure count before calling recordCheckResult.
+ assertEquals(retriesAllowed - 1, mPackageTracker.getCheckFailureCountForTests());
+
+ // On the last possible try, succeed.
+ CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken();
+ mPackageTracker.recordCheckResult(checkToken, true /* success */);
+
+ checkUpdateCheckSuccessful(currentPackageVersions);
+ }
+
+ /**
+ * Simulates reliability triggers happening too close together. Package tracker should ignore
+ * the ones it doesn't need.
+ */
+ @Test
+ public void trackingEnabled_reliabilityTrigger_tooSoon() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+
+ int retriesAllowed = 5;
+ int checkDelayMillis = 5 * 60 * 1000;
+ configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis);
+
+ PackageVersions oldPackageVersions = new PackageVersions(1, 1);
+ PackageVersions currentPackageVersions = new PackageVersions(2, 2);
+
+ // Simulate there being a newer version installed than the one recorded in storage.
+ configureValidApplications(currentPackageVersions);
+
+ // Force the storage into a state we want.
+ mPackageStatusStorage.forceCheckStateForTests(
+ PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(currentPackageVersions);
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+
+ // Increment the clock, but not enough.
+ mFakeClock.incrementClock(checkDelayMillis - 1);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did not trigger an update.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+ checkPackageStorageStatus(PackageStatus.CHECK_STARTED, currentPackageVersions);
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+
+ // Increment the clock slightly more. Should now consider the response overdue.
+ mFakeClock.incrementClock(2);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Triggering should have happened.
+ checkUpdateCheckTriggered(currentPackageVersions);
+ CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken();
+
+ // Check a new token was generated.
+ assertFalse(token1.equals(token2));
+ }
+
+ /**
+ * Tests what happens when a package update doesn't complete and a reliability trigger cleans
+ * up for it.
+ */
+ @Test
+ public void trackingEnabled_reliabilityTrigger_afterPackageUpdateDidNotComplete()
+ throws Exception {
+
+ // Set up device configuration.
+ configureTrackingEnabled();
+
+ int retriesAllowed = 5;
+ int checkDelayMillis = 5 * 60 * 1000;
+ configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis);
+
+ PackageVersions currentPackageVersions = new PackageVersions(1, 1);
+ PackageVersions newPackageVersions = new PackageVersions(2, 2);
+
+ // Simulate there being a newer version installed than the one recorded in storage.
+ configureValidApplications(currentPackageVersions);
+
+ // Force the storage into a state we want.
+ mPackageStatusStorage.forceCheckStateForTests(
+ PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions);
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Simulate a reliability trigger.
+ simulatePackageInstallation(newPackageVersions);
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(newPackageVersions);
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+
+ // Increment the clock, but not enough.
+ mFakeClock.incrementClock(checkDelayMillis + 1);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker triggered an update.
+ checkUpdateCheckTriggered(newPackageVersions);
+ CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken();
+
+ // Check a new token was generated.
+ assertFalse(token1.equals(token2));
+
+ // Simulate the reliability check completing.
+ mPackageTracker.recordCheckResult(token2, true /* success */);
+
+ // Check everything is now as it should be.
+ checkUpdateCheckSuccessful(newPackageVersions);
+ }
+
+ /**
+ * Simulates a reliability trigger happening too soon after a package update trigger occurred.
+ */
+ @Test
+ public void trackingEnabled_reliabilityTriggerAfterUpdate_tooSoon() throws Exception {
+ // Set up device configuration.
+ configureTrackingEnabled();
+
+ int retriesAllowed = 5;
+ int checkDelayMillis = 5 * 60 * 1000;
+ configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis);
+
+ PackageVersions currentPackageVersions = new PackageVersions(1, 1);
+ PackageVersions newPackageVersions = new PackageVersions(2, 2);
+
+ // Simulate there being a newer version installed than the one recorded in storage.
+ configureValidApplications(currentPackageVersions);
+
+ // Force the storage into a state we want.
+ mPackageStatusStorage.forceCheckStateForTests(
+ PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions);
+
+ // Initialize the package tracker.
+ mPackageTracker.start();
+
+ // Check the intent helper is properly configured.
+ checkIntentHelperInitializedAndReliabilityTrackingEnabled();
+
+ // Check the initial storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions);
+
+ // Simulate a package update trigger.
+ simulatePackageInstallation(newPackageVersions);
+
+ // Assert the PackageTracker did trigger an update.
+ checkUpdateCheckTriggered(newPackageVersions);
+ CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken();
+
+ // Increment the clock, but not enough.
+ mFakeClock.incrementClock(checkDelayMillis - 1);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Assert the PackageTracker did not trigger an update.
+ mFakeIntentHelper.assertUpdateNotTriggered();
+ checkPackageStorageStatus(PackageStatus.CHECK_STARTED, newPackageVersions);
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+
+ // Increment the clock slightly more. Should now consider the response overdue.
+ mFakeClock.incrementClock(2);
+
+ // Simulate a reliability trigger.
+ mFakeIntentHelper.simulateReliabilityTrigger();
+
+ // Triggering should have happened.
+ checkUpdateCheckTriggered(newPackageVersions);
+ CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken();
+
+ // Check a new token was generated.
+ assertFalse(token1.equals(token2));
+ }
+
+ private void simulatePackageInstallation(PackageVersions packageVersions) throws Exception {
+ configureApplicationsValidManifests(packageVersions);
+
+ // Simulate a tracked package being updated.
+ mFakeIntentHelper.simulatePackageUpdatedEvent();
+ }
+
+ /**
+ * Checks an update check was triggered, reliability triggering is therefore enabled and the
+ * storage state reflects that there is a check in progress.
+ */
+ private void checkUpdateCheckTriggered(PackageVersions packageVersions) {
+ // Assert the PackageTracker attempted to trigger an update.
+ mFakeIntentHelper.assertUpdateTriggered();
+
+ // If an update check was triggered reliability triggering should always be enabled to
+ // ensure that it can be completed if it fails.
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+
+ // Check the expected storage state.
+ checkPackageStorageStatus(PackageStatus.CHECK_STARTED, packageVersions);
+ }
+
+ private void checkUpdateCheckFailed(PackageVersions packageVersions) {
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+
+ // Assert the storage was updated.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, packageVersions);
+ }
+
+ private void checkUpdateCheckSuccessful(PackageVersions packageVersions) {
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+
+ // Assert the storage was updated.
+ checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
+
+ // Peek inside the package tracker to make sure it is tracking failure counts properly.
+ assertEquals(0, mPackageTracker.getCheckFailureCountForTests());
+ }
+
+ private PackageVersions configureValidApplications() throws Exception {
+ configureValidApplications(INITIAL_APP_PACKAGE_VERSIONS);
+ return INITIAL_APP_PACKAGE_VERSIONS;
+ }
+
+ private void configureValidApplications(PackageVersions versions) throws Exception {
+ configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME);
+ configureDataAppPackageOk(DATA_APP_PACKAGE_NAME);
+ configureApplicationsValidManifests(versions);
+ }
+
+ private void configureApplicationsValidManifests(PackageVersions versions) throws Exception {
+ configureUpdateAppManifestOk(UPDATE_APP_PACKAGE_NAME);
+ configureDataAppManifestOk(DATA_APP_PACKAGE_NAME);
+ configureUpdateAppPackageVersion(UPDATE_APP_PACKAGE_NAME, versions.mUpdateAppVersion);
+ configureDataAppPackageVersion(DATA_APP_PACKAGE_NAME, versions.mDataAppVersion);
+ }
+
+ private void configureUpdateAppPackageVersion(String updateAppPackageName,
+ int updataAppPackageVersion) throws Exception {
+ when(mMockPackageManagerHelper.getInstalledPackageVersion(updateAppPackageName))
+ .thenReturn(updataAppPackageVersion);
+ }
+
+ private void configureDataAppPackageVersion(String dataAppPackageName,
+ int dataAppPackageVersion) throws Exception {
+ when(mMockPackageManagerHelper.getInstalledPackageVersion(dataAppPackageName))
+ .thenReturn(dataAppPackageVersion);
+ }
+
+ private void configureUpdateAppManifestOk(String updateAppPackageName) throws Exception {
+ Intent expectedIntent = RulesUpdaterContract.createUpdaterIntent(updateAppPackageName);
+ when(mMockPackageManagerHelper.receiverRegistered(
+ filterEquals(expectedIntent),
+ eq(RulesUpdaterContract.TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION)))
+ .thenReturn(true);
+ when(mMockPackageManagerHelper.usesPermission(
+ updateAppPackageName, RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION))
+ .thenReturn(true);
+ }
+
+ private void configureUpdateAppManifestBad(String updateAppPackageName) throws Exception {
+ Intent expectedIntent = RulesUpdaterContract.createUpdaterIntent(updateAppPackageName);
+ when(mMockPackageManagerHelper.receiverRegistered(
+ filterEquals(expectedIntent),
+ eq(RulesUpdaterContract.TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION)))
+ .thenReturn(false);
+ // Has permission, but that shouldn't matter if the check above is false.
+ when(mMockPackageManagerHelper.usesPermission(
+ updateAppPackageName, RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION))
+ .thenReturn(true);
+ }
+
+ private void configureDataAppManifestOk(String dataAppPackageName) throws Exception {
+ when(mMockPackageManagerHelper.contentProviderRegistered(
+ TimeZoneRulesDataContract.AUTHORITY, dataAppPackageName))
+ .thenReturn(true);
+ }
+
+ private void configureDataAppManifestBad(String dataAppPackageName) throws Exception {
+ // Simulate the data app not exposing the content provider we require.
+ when(mMockPackageManagerHelper.contentProviderRegistered(
+ TimeZoneRulesDataContract.AUTHORITY, dataAppPackageName))
+ .thenReturn(false);
+ }
+
+ private void configureTrackingEnabled() {
+ when(mMockConfigHelper.isTrackingEnabled()).thenReturn(true);
+ }
+
+ private void configureTrackingDisabled() {
+ when(mMockConfigHelper.isTrackingEnabled()).thenReturn(false);
+ }
+
+ private void configureReliabilityConfigSettings(int retriesAllowed, int checkDelayMillis) {
+ when(mMockConfigHelper.getFailedCheckRetryCount()).thenReturn(retriesAllowed);
+ when(mMockConfigHelper.getCheckTimeAllowedMillis()).thenReturn(checkDelayMillis);
+ }
+
+ private void configureReliabilityConfigSettingsOk() {
+ configureReliabilityConfigSettings(5, 5 * 60 * 1000);
+ }
+
+ private void configureUpdateAppPackageOk(String updateAppPackageName) throws Exception {
+ when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(updateAppPackageName);
+ when(mMockPackageManagerHelper.isPrivilegedApp(updateAppPackageName)).thenReturn(true);
+ }
+
+ private void configureUpdateAppPackageNotPrivileged(String updateAppPackageName)
+ throws Exception {
+ when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(updateAppPackageName);
+ when(mMockPackageManagerHelper.isPrivilegedApp(updateAppPackageName)).thenReturn(false);
+ }
+
+ private void configureUpdateAppPackageNameMissing() {
+ when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(null);
+ }
+
+ private void configureDataAppPackageOk(String dataAppPackageName) throws Exception {
+ when(mMockConfigHelper.getDataAppPackageName()).thenReturn(dataAppPackageName);
+ when(mMockPackageManagerHelper.isPrivilegedApp(dataAppPackageName)).thenReturn(true);
+ }
+
+ private void configureDataAppPackageNotPrivileged(String dataAppPackageName)
+ throws Exception {
+ when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(dataAppPackageName);
+ when(mMockPackageManagerHelper.isPrivilegedApp(dataAppPackageName)).thenReturn(false);
+ }
+
+ private void configureDataAppPackageNameMissing() {
+ when(mMockConfigHelper.getDataAppPackageName()).thenThrow(new RuntimeException());
+ }
+
+ private void checkIntentHelperInitializedAndReliabilityTrackingEnabled() {
+ // Verify that calling start initialized the IntentHelper as well.
+ mFakeIntentHelper.assertInitialized(UPDATE_APP_PACKAGE_NAME, DATA_APP_PACKAGE_NAME);
+
+ // Assert that reliability tracking is always enabled after initialization.
+ mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ }
+
+ private void checkPackageStorageStatus(
+ int expectedCheckStatus, PackageVersions expectedPackageVersions) {
+ PackageStatus packageStatus = mPackageStatusStorage.getPackageStatus();
+ assertEquals(expectedCheckStatus, packageStatus.mCheckStatus);
+ assertEquals(expectedPackageVersions, packageStatus.mVersions);
+ }
+
+ private void checkPackageStorageStatusIsInitialOrReset() {
+ assertNull(mPackageStatusStorage.getPackageStatus());
+ }
+
+ private static CheckToken createArbitraryCheckToken() {
+ return new CheckToken(1, INITIAL_APP_PACKAGE_VERSIONS);
+ }
+
+ /**
+ * A fake IntentHelper implementation for use in tests.
+ */
+ private static class FakeIntentHelper implements IntentHelper {
+
+ private Listener mListener;
+ private String mUpdateAppPackageName;
+ private String mDataAppPackageName;
+
+ private CheckToken mLastToken;
+
+ private boolean mReliabilityTriggeringEnabled;
+
+ @Override
+ public void initialize(String updateAppPackageName, String dataAppPackageName,
+ Listener listener) {
+ assertNotNull(updateAppPackageName);
+ assertNotNull(dataAppPackageName);
+ assertNotNull(listener);
+ mListener = listener;
+ mUpdateAppPackageName = updateAppPackageName;
+ mDataAppPackageName = dataAppPackageName;
+ }
+
+ public void assertInitialized(
+ String expectedUpdateAppPackageName, String expectedDataAppPackageName) {
+ assertNotNull(mListener);
+ assertEquals(expectedUpdateAppPackageName, mUpdateAppPackageName);
+ assertEquals(expectedDataAppPackageName, mDataAppPackageName);
+ }
+
+ public void assertNotInitialized() {
+ assertNull(mListener);
+ }
+
+ @Override
+ public void sendTriggerUpdateCheck(CheckToken checkToken) {
+ if (mLastToken != null) {
+ fail("lastToken already set");
+ }
+ mLastToken = checkToken;
+ }
+
+ @Override
+ public void enableReliabilityTriggering() {
+ mReliabilityTriggeringEnabled = true;
+ }
+
+ @Override
+ public void disableReliabilityTriggering() {
+ mReliabilityTriggeringEnabled = false;
+ }
+
+ public void assertReliabilityTriggeringEnabled() {
+ assertTrue(mReliabilityTriggeringEnabled);
+ }
+
+ public void assertReliabilityTriggeringDisabled() {
+ assertFalse(mReliabilityTriggeringEnabled);
+ }
+
+ public void assertUpdateTriggered() {
+ assertNotNull(mLastToken);
+ }
+
+ public void assertUpdateNotTriggered() {
+ assertNull(mLastToken);
+ }
+
+ public CheckToken captureAndResetLastToken() {
+ CheckToken toReturn = mLastToken;
+ assertNotNull("No update triggered", toReturn);
+ mLastToken = null;
+ return toReturn;
+ }
+
+ public void simulatePackageUpdatedEvent() {
+ mListener.triggerUpdateIfNeeded(true);
+ }
+
+ public void simulateReliabilityTrigger() {
+ mListener.triggerUpdateIfNeeded(false);
+ }
+ }
+
+ private static class FakeClockHelper implements ClockHelper {
+
+ private long currentTime = 1000;
+
+ @Override
+ public long currentTimestamp() {
+ return currentTime;
+ }
+
+ public void incrementClock(long millis) {
+ currentTime += millis;
+ }
+ }
+
+ /**
+ * Registers a mockito parameter matcher that uses {@link Intent#filterEquals(Intent)}. to
+ * check the parameter against the intent supplied.
+ */
+ private static Intent filterEquals(final Intent expected) {
+ final Matcher<Intent> m = new BaseMatcher<Intent>() {
+ @Override
+ public boolean matches(Object actual) {
+ return actual != null && expected.filterEquals((Intent) actual);
+ }
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(expected.toString());
+ }
+ };
+ return argThat(m);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageVersionsTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageVersionsTest.java
new file mode 100644
index 0000000..a470f8f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageVersionsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import org.junit.Test;
+
+import android.support.test.filters.SmallTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+@SmallTest
+public class PackageVersionsTest {
+
+ @Test
+ public void equals() {
+ PackageVersions baseline =
+ new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */);
+ assertEquals(baseline, baseline);
+
+ PackageVersions deepEqual =
+ new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */);
+ assertEquals(baseline, deepEqual);
+
+ PackageVersions differentUpdateAppVersion =
+ new PackageVersions(2 /* updateAppVersion */, 1 /* dataAppVersion */);
+ assertFalse(baseline.equals(differentUpdateAppVersion));
+
+ PackageVersions differentDataAppVersion =
+ new PackageVersions(1 /* updateAppVersion */, 2 /* dataAppVersion */);
+ assertFalse(baseline.equals(differentDataAppVersion));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
new file mode 100644
index 0000000..a7f4c99
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezone;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import android.app.timezone.Callback;
+import android.app.timezone.DistroRulesVersion;
+import android.app.timezone.ICallback;
+import android.app.timezone.RulesManager;
+import android.app.timezone.RulesState;
+import android.os.ParcelFileDescriptor;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import javax.annotation.Nullable;
+import libcore.tzdata.shared2.DistroVersion;
+import libcore.tzdata.shared2.StagedDistroOperation;
+import libcore.tzdata.update2.TimeZoneDistroInstaller;
+
+import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * White box interaction / unit testing of the {@link RulesManagerService}.
+ */
+public class RulesManagerServiceTest {
+
+ private RulesManagerService mRulesManagerService;
+
+ private FakeExecutor mFakeExecutor;
+ private PermissionHelper mMockPermissionHelper;
+ private FileDescriptorHelper mMockFileDescriptorHelper;
+ private PackageTracker mMockPackageTracker;
+ private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller;
+
+ @Before
+ public void setUp() {
+ mFakeExecutor = new FakeExecutor();
+
+ mMockFileDescriptorHelper = mock(FileDescriptorHelper.class);
+ mMockPackageTracker = mock(PackageTracker.class);
+ mMockPermissionHelper = mock(PermissionHelper.class);
+ mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class);
+
+ mRulesManagerService = new RulesManagerService(
+ mMockPermissionHelper,
+ mFakeExecutor,
+ mMockFileDescriptorHelper,
+ mMockPackageTracker,
+ mMockTimeZoneDistroInstaller);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void getRulesState_noCallerPermission() throws Exception {
+ configureCallerDoesNotHavePermission();
+ mRulesManagerService.getRulesState();
+ }
+
+ @Test(expected = SecurityException.class)
+ public void requestInstall_noCallerPermission() throws Exception {
+ configureCallerDoesNotHavePermission();
+ mRulesManagerService.requestInstall(null, null, null);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void requestUninstall_noCallerPermission() throws Exception {
+ configureCallerDoesNotHavePermission();
+ mRulesManagerService.requestUninstall(null, null);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void requestNothing_noCallerPermission() throws Exception {
+ configureCallerDoesNotHavePermission();
+ mRulesManagerService.requestNothing(null, true);
+ }
+
+ @Test
+ public void getRulesState_systemRulesError() throws Exception {
+ configureDeviceCannotReadSystemRulesVersion();
+
+ assertNull(mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void getRulesState_stagedInstall() throws Exception {
+ configureCallerHasPermission();
+
+ configureDeviceSystemRulesVersion("2016a");
+
+ DistroVersion stagedDistroVersion = new DistroVersion(
+ DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
+ DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
+ "2016c",
+ 3);
+ configureStagedInstall(stagedDistroVersion);
+
+ DistroVersion installedDistroVersion = new DistroVersion(
+ DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
+ DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
+ "2016b",
+ 4);
+ configureInstalledDistroVersion(installedDistroVersion);
+
+ DistroRulesVersion stagedDistroRulesVersion = new DistroRulesVersion(
+ stagedDistroVersion.rulesVersion, stagedDistroVersion.revision);
+ DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
+ installedDistroVersion.rulesVersion, installedDistroVersion.revision);
+ RulesState expectedRuleState = new RulesState(
+ "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_INSTALL, stagedDistroRulesVersion,
+ RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
+ assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void getRulesState_nothingStaged() throws Exception {
+ configureCallerHasPermission();
+
+ configureDeviceSystemRulesVersion("2016a");
+
+ configureNoStagedOperation();
+
+ DistroVersion installedDistroVersion = new DistroVersion(
+ DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
+ DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
+ "2016b",
+ 4);
+ configureInstalledDistroVersion(installedDistroVersion);
+
+ DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
+ installedDistroVersion.rulesVersion, installedDistroVersion.revision);
+ RulesState expectedRuleState = new RulesState(
+ "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
+ assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void getRulesState_uninstallStaged() throws Exception {
+ configureCallerHasPermission();
+
+ configureDeviceSystemRulesVersion("2016a");
+
+ configureStagedUninstall();
+
+ DistroVersion installedDistroVersion = new DistroVersion(
+ DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
+ DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
+ "2016b",
+ 4);
+ configureInstalledDistroVersion(installedDistroVersion);
+
+ DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
+ installedDistroVersion.rulesVersion, installedDistroVersion.revision);
+ RulesState expectedRuleState = new RulesState(
+ "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNINSTALL, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
+ assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void getRulesState_installedRulesError() throws Exception {
+ configureCallerHasPermission();
+
+ String systemRulesVersion = "2016a";
+ configureDeviceSystemRulesVersion(systemRulesVersion);
+
+ configureStagedUninstall();
+ configureDeviceCannotReadInstalledDistroVersion();
+
+ RulesState expectedRuleState = new RulesState(
+ "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNINSTALL, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
+ assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void getRulesState_stagedRulesError() throws Exception {
+ configureCallerHasPermission();
+
+ String systemRulesVersion = "2016a";
+ configureDeviceSystemRulesVersion(systemRulesVersion);
+
+ configureDeviceCannotReadStagedDistroOperation();
+
+ DistroVersion installedDistroVersion = new DistroVersion(
+ DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
+ DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
+ "2016b",
+ 4);
+ configureInstalledDistroVersion(installedDistroVersion);
+
+ DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
+ installedDistroVersion.rulesVersion, installedDistroVersion.revision);
+ RulesState expectedRuleState = new RulesState(
+ "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
+ assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void getRulesState_noInstalledRules() throws Exception {
+ configureCallerHasPermission();
+
+ String systemRulesVersion = "2016a";
+ configureDeviceSystemRulesVersion(systemRulesVersion);
+ configureNoStagedOperation();
+ configureInstalledDistroVersion(null);
+
+ RulesState expectedRuleState = new RulesState(
+ systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ false /* operationInProgress */,
+ RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
+ assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void getRulesState_operationInProgress() throws Exception {
+ configureCallerHasPermission();
+
+ String systemRulesVersion = "2016a";
+ String installedRulesVersion = "2016b";
+ int revision = 3;
+
+ configureDeviceSystemRulesVersion(systemRulesVersion);
+
+ DistroVersion installedDistroVersion = new DistroVersion(
+ DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
+ DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
+ installedRulesVersion,
+ revision);
+ configureInstalledDistroVersion(installedDistroVersion);
+
+ byte[] expectedContent = createArbitraryBytes(1000);
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
+
+ // Start an async operation so there is one in progress. The mFakeExecutor won't actually
+ // execute it.
+ byte[] tokenBytes = createArbitraryTokenBytes();
+ ICallback callback = new StubbedCallback();
+
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback);
+
+ RulesState expectedRuleState = new RulesState(
+ systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+ true /* operationInProgress */,
+ RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
+ RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
+ assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
+ }
+
+ @Test
+ public void requestInstall_operationInProgress() throws Exception {
+ configureCallerHasPermission();
+
+ byte[] expectedContent = createArbitraryBytes(1000);
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
+
+ byte[] tokenBytes = createArbitraryTokenBytes();
+ ICallback callback = new StubbedCallback();
+
+ // First request should succeed.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
+
+ // Something async should be enqueued. Clear it but do not execute it so we can detect the
+ // second request does nothing.
+ mFakeExecutor.getAndResetLastCommand();
+
+ // Second request should fail.
+ assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS,
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestInstall_badToken() throws Exception {
+ configureCallerHasPermission();
+
+ byte[] expectedContent = createArbitraryBytes(1000);
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
+
+ byte[] badTokenBytes = new byte[2];
+ ICallback callback = new StubbedCallback();
+
+ try {
+ mRulesManagerService.requestInstall(parcelFileDescriptor, badTokenBytes, callback);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestInstall_nullParcelFileDescriptor() throws Exception {
+ configureCallerHasPermission();
+
+ ParcelFileDescriptor parcelFileDescriptor = null;
+ byte[] tokenBytes = createArbitraryTokenBytes();
+ ICallback callback = new StubbedCallback();
+
+ try {
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestInstall_nullCallback() throws Exception {
+ configureCallerHasPermission();
+
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ byte[] tokenBytes = createArbitraryTokenBytes();
+ ICallback callback = null;
+
+ try {
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestInstall_asyncSuccess() throws Exception {
+ configureCallerHasPermission();
+
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ byte[] expectedContent = createArbitraryBytes(1000);
+ configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ TestCallback callback = new TestCallback();
+
+ // Request the install.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
+
+ // Assert nothing has happened yet.
+ callback.assertNoResultReceived();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+
+ // Set up the installer.
+ configureStageInstallExpectation(expectedContent, TimeZoneDistroInstaller.INSTALL_SUCCESS);
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify the expected calls were made to other components.
+ verifyStageInstallCalled(expectedContent);
+ verifyPackageTrackerCalled(token, true /* success */);
+
+ // Check the callback was called.
+ callback.assertResultReceived(Callback.SUCCESS);
+ }
+
+ @Test
+ public void requestInstall_nullTokenBytes() throws Exception {
+ configureCallerHasPermission();
+
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ byte[] expectedContent = createArbitraryBytes(1000);
+ configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
+
+ TestCallback callback = new TestCallback();
+
+ // Request the install.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestInstall(
+ parcelFileDescriptor, null /* tokenBytes */, callback));
+
+ // Assert nothing has happened yet.
+ verifyNoInstallerCallsMade();
+ callback.assertNoResultReceived();
+
+ // Set up the installer.
+ configureStageInstallExpectation(expectedContent, TimeZoneDistroInstaller.INSTALL_SUCCESS);
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify the expected calls were made to other components.
+ verifyStageInstallCalled(expectedContent);
+ verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
+
+ // Check the callback was received.
+ callback.assertResultReceived(Callback.SUCCESS);
+ }
+
+ @Test
+ public void requestInstall_asyncInstallFail() throws Exception {
+ configureCallerHasPermission();
+
+ byte[] expectedContent = createArbitraryBytes(1000);
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ configureParcelFileDescriptorReadSuccess(parcelFileDescriptor, expectedContent);
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ TestCallback callback = new TestCallback();
+
+ // Request the install.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
+
+ // Assert nothing has happened yet.
+ verifyNoInstallerCallsMade();
+ callback.assertNoResultReceived();
+
+ // Set up the installer.
+ configureStageInstallExpectation(
+ expectedContent, TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR);
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify the expected calls were made to other components.
+ verifyStageInstallCalled(expectedContent);
+
+ // Validation failure is treated like a successful check: repeating it won't improve things.
+ boolean expectedSuccess = true;
+ verifyPackageTrackerCalled(token, expectedSuccess);
+
+ // Check the callback was received.
+ callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR);
+ }
+
+ @Test
+ public void requestInstall_asyncParcelFileDescriptorReadFail() throws Exception {
+ configureCallerHasPermission();
+
+ ParcelFileDescriptor parcelFileDescriptor = createFakeParcelFileDescriptor();
+ configureParcelFileDescriptorReadFailure(parcelFileDescriptor);
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ TestCallback callback = new TestCallback();
+
+ // Request the install.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify nothing else happened.
+ verifyNoInstallerCallsMade();
+
+ // A failure to read the ParcelFileDescriptor is treated as a failure. It might be the
+ // result of a file system error. This is a fairly arbitrary choice.
+ verifyPackageTrackerCalled(token, false /* success */);
+
+ verifyNoPackageTrackerCallsMade();
+
+ // Check the callback was received.
+ callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE);
+ }
+
+ @Test
+ public void requestUninstall_operationInProgress() throws Exception {
+ configureCallerHasPermission();
+
+ byte[] tokenBytes = createArbitraryTokenBytes();
+ ICallback callback = new StubbedCallback();
+
+ // First request should succeed.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestUninstall(tokenBytes, callback));
+
+ // Something async should be enqueued. Clear it but do not execute it so we can detect the
+ // second request does nothing.
+ mFakeExecutor.getAndResetLastCommand();
+
+ // Second request should fail.
+ assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS,
+ mRulesManagerService.requestUninstall(tokenBytes, callback));
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestUninstall_badToken() throws Exception {
+ configureCallerHasPermission();
+
+ byte[] badTokenBytes = new byte[2];
+ ICallback callback = new StubbedCallback();
+
+ try {
+ mRulesManagerService.requestUninstall(badTokenBytes, callback);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestUninstall_nullCallback() throws Exception {
+ configureCallerHasPermission();
+
+ byte[] tokenBytes = createArbitraryTokenBytes();
+ ICallback callback = null;
+
+ try {
+ mRulesManagerService.requestUninstall(tokenBytes, callback);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestUninstall_asyncSuccess() throws Exception {
+ configureCallerHasPermission();
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ TestCallback callback = new TestCallback();
+
+ // Request the uninstall.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestUninstall(tokenBytes, callback));
+
+ // Assert nothing has happened yet.
+ callback.assertNoResultReceived();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+
+ // Set up the installer.
+ configureStageUninstallExpectation(true /* success */);
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify the expected calls were made to other components.
+ verifyStageUninstallCalled();
+ verifyPackageTrackerCalled(token, true /* success */);
+
+ // Check the callback was called.
+ callback.assertResultReceived(Callback.SUCCESS);
+ }
+
+ @Test
+ public void requestUninstall_nullTokenBytes() throws Exception {
+ configureCallerHasPermission();
+
+ TestCallback callback = new TestCallback();
+
+ // Request the uninstall.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestUninstall(null /* tokenBytes */, callback));
+
+ // Assert nothing has happened yet.
+ verifyNoInstallerCallsMade();
+ callback.assertNoResultReceived();
+
+ // Set up the installer.
+ configureStageUninstallExpectation(true /* success */);
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify the expected calls were made to other components.
+ verifyStageUninstallCalled();
+ verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
+
+ // Check the callback was received.
+ callback.assertResultReceived(Callback.SUCCESS);
+ }
+
+ @Test
+ public void requestUninstall_asyncUninstallFail() throws Exception {
+ configureCallerHasPermission();
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ TestCallback callback = new TestCallback();
+
+ // Request the uninstall.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestUninstall(tokenBytes, callback));
+
+ // Assert nothing has happened yet.
+ verifyNoInstallerCallsMade();
+ callback.assertNoResultReceived();
+
+ // Set up the installer.
+ configureStageUninstallExpectation(false /* success */);
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify the expected calls were made to other components.
+ verifyStageUninstallCalled();
+ verifyPackageTrackerCalled(token, false /* success */);
+
+ // Check the callback was received.
+ callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE);
+ }
+
+ @Test
+ public void requestNothing_operationInProgressOk() throws Exception {
+ configureCallerHasPermission();
+
+ // Set up a parallel operation.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestUninstall(null, new StubbedCallback()));
+ // Something async should be enqueued. Clear it but do not execute it to simulate it still
+ // being in progress.
+ mFakeExecutor.getAndResetLastCommand();
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ // Make the call.
+ mRulesManagerService.requestNothing(tokenBytes, true /* success */);
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+
+ // Verify the expected calls were made to other components.
+ verifyPackageTrackerCalled(token, true /* success */);
+ verifyNoInstallerCallsMade();
+ }
+
+ @Test
+ public void requestNothing_badToken() throws Exception {
+ configureCallerHasPermission();
+
+ byte[] badTokenBytes = new byte[2];
+
+ try {
+ mRulesManagerService.requestNothing(badTokenBytes, true /* success */);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Assert nothing async was enqueued.
+ mFakeExecutor.assertNothingQueued();
+
+ // Assert no other calls were made.
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+ }
+
+ @Test
+ public void requestNothing() throws Exception {
+ configureCallerHasPermission();
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ // Make the call.
+ mRulesManagerService.requestNothing(tokenBytes, false /* success */);
+
+ // Assert everything required was done.
+ verifyNoInstallerCallsMade();
+ verifyPackageTrackerCalled(token, false /* success */);
+ }
+
+ @Test
+ public void requestNothing_nullTokenBytes() throws Exception {
+ configureCallerHasPermission();
+
+ // Make the call.
+ mRulesManagerService.requestNothing(null /* tokenBytes */, true /* success */);
+
+ // Assert everything required was done.
+ verifyNoInstallerCallsMade();
+ verifyPackageTrackerCalled(null /* token */, true /* success */);
+ }
+
+ private void verifyNoPackageTrackerCallsMade() {
+ verifyNoMoreInteractions(mMockPackageTracker);
+ reset(mMockPackageTracker);
+ }
+
+ private void verifyPackageTrackerCalled(
+ CheckToken expectedCheckToken, boolean expectedSuccess) {
+ verify(mMockPackageTracker).recordCheckResult(expectedCheckToken, expectedSuccess);
+ reset(mMockPackageTracker);
+ }
+
+ private void configureCallerHasPermission() throws Exception {
+ doNothing()
+ .when(mMockPermissionHelper)
+ .enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
+ }
+
+ private void configureCallerDoesNotHavePermission() {
+ doThrow(new SecurityException("Simulated permission failure"))
+ .when(mMockPermissionHelper)
+ .enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
+ }
+
+ private void configureParcelFileDescriptorReadSuccess(ParcelFileDescriptor parcelFileDescriptor,
+ byte[] content) throws Exception {
+ when(mMockFileDescriptorHelper.readFully(parcelFileDescriptor)).thenReturn(content);
+ }
+
+ private void configureParcelFileDescriptorReadFailure(ParcelFileDescriptor parcelFileDescriptor)
+ throws Exception {
+ when(mMockFileDescriptorHelper.readFully(parcelFileDescriptor))
+ .thenThrow(new IOException("Simulated failure"));
+ }
+
+ private void configureStageInstallExpectation(byte[] expectedContent, int resultCode)
+ throws Exception {
+ when(mMockTimeZoneDistroInstaller.stageInstallWithErrorCode(eq(expectedContent)))
+ .thenReturn(resultCode);
+ }
+
+ private void configureStageUninstallExpectation(boolean success) throws Exception {
+ doReturn(success).when(mMockTimeZoneDistroInstaller).stageUninstall();
+ }
+
+ private void verifyStageInstallCalled(byte[] expectedContent) throws Exception {
+ verify(mMockTimeZoneDistroInstaller).stageInstallWithErrorCode(eq(expectedContent));
+ verifyNoMoreInteractions(mMockTimeZoneDistroInstaller);
+ reset(mMockTimeZoneDistroInstaller);
+ }
+
+ private void verifyStageUninstallCalled() throws Exception {
+ verify(mMockTimeZoneDistroInstaller).stageUninstall();
+ verifyNoMoreInteractions(mMockTimeZoneDistroInstaller);
+ reset(mMockTimeZoneDistroInstaller);
+ }
+
+ private void verifyNoInstallerCallsMade() {
+ verifyNoMoreInteractions(mMockTimeZoneDistroInstaller);
+ reset(mMockTimeZoneDistroInstaller);
+ }
+
+ private static byte[] createArbitraryBytes(int length) {
+ byte[] bytes = new byte[length];
+ for (int i = 0; i < length; i++) {
+ bytes[i] = (byte) i;
+ }
+ return bytes;
+ }
+
+ private byte[] createArbitraryTokenBytes() {
+ return createArbitraryToken().toByteArray();
+ }
+
+ private CheckToken createArbitraryToken() {
+ return new CheckToken(1, new PackageVersions(1, 1));
+ }
+
+ private ParcelFileDescriptor createFakeParcelFileDescriptor() {
+ return new ParcelFileDescriptor((ParcelFileDescriptor) null);
+ }
+
+ private void configureDeviceSystemRulesVersion(String systemRulesVersion) throws Exception {
+ when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn(systemRulesVersion);
+ }
+
+ private void configureInstalledDistroVersion(@Nullable DistroVersion installedDistroVersion)
+ throws Exception {
+ when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion())
+ .thenReturn(installedDistroVersion);
+ }
+
+ private void configureStagedInstall(DistroVersion stagedDistroVersion) throws Exception {
+ when(mMockTimeZoneDistroInstaller.getStagedDistroOperation())
+ .thenReturn(StagedDistroOperation.install(stagedDistroVersion));
+ }
+
+ private void configureStagedUninstall() throws Exception {
+ when(mMockTimeZoneDistroInstaller.getStagedDistroOperation())
+ .thenReturn(StagedDistroOperation.uninstall());
+ }
+
+ private void configureNoStagedOperation() throws Exception {
+ when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn(null);
+ }
+
+ private void configureDeviceCannotReadStagedDistroOperation() throws Exception {
+ when(mMockTimeZoneDistroInstaller.getStagedDistroOperation())
+ .thenThrow(new IOException("Simulated failure"));
+ }
+
+ private void configureDeviceCannotReadSystemRulesVersion() throws Exception {
+ when(mMockTimeZoneDistroInstaller.getSystemRulesVersion())
+ .thenThrow(new IOException("Simulated failure"));
+ }
+
+ private void configureDeviceCannotReadInstalledDistroVersion() throws Exception {
+ when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion())
+ .thenThrow(new IOException("Simulated failure"));
+ }
+
+ private static class FakeExecutor implements Executor {
+
+ private Runnable mLastCommand;
+
+ @Override
+ public void execute(Runnable command) {
+ assertNull(mLastCommand);
+ assertNotNull(command);
+ mLastCommand = command;
+ }
+
+ public Runnable getAndResetLastCommand() {
+ assertNotNull(mLastCommand);
+ Runnable toReturn = mLastCommand;
+ mLastCommand = null;
+ return toReturn;
+ }
+
+ public void simulateAsyncExecutionOfLastCommand() {
+ Runnable toRun = getAndResetLastCommand();
+ toRun.run();
+ }
+
+ public void assertNothingQueued() {
+ assertNull(mLastCommand);
+ }
+ }
+
+ private static class TestCallback extends ICallback.Stub {
+
+ private boolean mOnFinishedCalled;
+ private int mLastError;
+
+ @Override
+ public void onFinished(int error) {
+ assertFalse(mOnFinishedCalled);
+ mOnFinishedCalled = true;
+ mLastError = error;
+ }
+
+ public void assertResultReceived(int expectedResult) {
+ assertTrue(mOnFinishedCalled);
+ assertEquals(expectedResult, mLastError);
+ }
+
+ public void assertNoResultReceived() {
+ assertFalse(mOnFinishedCalled);
+ }
+ }
+
+ private static class StubbedCallback extends ICallback.Stub {
+ @Override
+ public void onFinished(int error) {
+ fail("Unexpected call");
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index a23a6b22..649de4a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -33,8 +34,7 @@
* runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest
*/
@SmallTest
-// TODO(b/35196891): Add back to presubmit once the bug is fixed.
-//@Presubmit
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 2b4d9fb..f253632 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.TaskSnapshotController.*;
import static junit.framework.Assert.assertEquals;
@@ -76,12 +77,19 @@
public void testGetSnapshotMode() throws Exception {
final WindowState disabledWindow = createWindow(null,
FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
- disabledWindow.mAppToken.setDisablePreviewSnapshots(true);
+ disabledWindow.mAppToken.setDisablePreviewScreenshots(true);
assertEquals(SNAPSHOT_MODE_APP_THEME,
sWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
+
final WindowState normalWindow = createWindow(null,
FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
assertEquals(SNAPSHOT_MODE_REAL,
sWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
+
+ final WindowState secureWindow = createWindow(null,
+ FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow");
+ secureWindow.mAttrs.flags |= FLAG_SECURE;
+ assertEquals(SNAPSHOT_MODE_APP_THEME,
+ sWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 18d0c32..8146763 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -68,7 +68,11 @@
}
private void cleanDirectory() {
- for (File file : new File(sFilesDir, "snapshots").listFiles()) {
+ final File[] files = new File(sFilesDir, "snapshots").listFiles();
+ if (files == null) {
+ return;
+ }
+ for (File file : files) {
if (!file.isDirectory()) {
file.delete();
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 717ddf2..e2868d7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -171,11 +171,25 @@
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100), 10);
+ mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground(
+ mockCanvas, new Rect(0, 0, 50, 100), 10);
verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
}
@Test
+ public void testDrawStatusBarBackground_nullFrame() {
+ setupSurface(100, 100);
+ final Rect insets = new Rect(0, 10, 10, 0);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ final Canvas mockCanvas = mock(Canvas.class);
+ when(mockCanvas.getWidth()).thenReturn(100);
+ when(mockCanvas.getHeight()).thenReturn(100);
+ mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground(
+ mockCanvas, null, 10);
+ verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
+ }
+
+ @Test
public void testDrawStatusBarBackground_nope() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
@@ -183,7 +197,8 @@
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100), 10);
+ mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground(
+ mockCanvas, new Rect(0, 0, 100, 100), 10);
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
}
@@ -196,7 +211,7 @@
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawNavigationBarBackground(mockCanvas);
+ mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas);
verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
}
@@ -209,7 +224,7 @@
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawNavigationBarBackground(mockCanvas);
+ mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas);
verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
}
@@ -222,7 +237,7 @@
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
- mSurface.drawNavigationBarBackground(mockCanvas);
+ mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas);
verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c20eb30..984344c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5371,9 +5371,10 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.isDataConnectivityPossible();
+ return telephony.isDataConnectivityPossible(getSubId(SubscriptionManager
+ .getDefaultDataSubscriptionId()));
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#isDataConnectivityPossible", e);
+ Log.e(TAG, "Error calling ITelephony#isDataAllowed", e);
}
return false;
}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index f01e4c0..256e13b 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -106,6 +106,7 @@
public static final int EVENT_REDIRECTION_DETECTED = BASE + 44;
public static final int EVENT_PCO_DATA_RECEIVED = BASE + 45;
public static final int EVENT_SET_CARRIER_DATA_ENABLED = BASE + 46;
+ public static final int EVENT_DATA_RECONNECT = BASE + 47;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 621ccd6..bbfc490 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -370,7 +370,7 @@
/**
* Report whether data connectivity is possible.
*/
- boolean isDataConnectivityPossible();
+ boolean isDataConnectivityPossible(int subId);
Bundle getCellLocation(String callingPkg);
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 504d54e..3af0adc 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -52,7 +52,6 @@
libtinyxml2 \
libvintf \
libhwbinder \
- android.hidl.base@1.0 \
android.hidl.token@1.0
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/tests/radio/src/android/hardware/radio/tests/RadioTest.java b/tests/radio/src/android/hardware/radio/tests/RadioTest.java
index 7ab5618..82b2903 100644
--- a/tests/radio/src/android/hardware/radio/tests/RadioTest.java
+++ b/tests/radio/src/android/hardware/radio/tests/RadioTest.java
@@ -92,6 +92,10 @@
}
private void openTuner() {
+ openTuner(true);
+ }
+
+ private void openTuner(boolean withAudio) {
assertNull(mRadioTuner);
// find FM band and build its config
@@ -109,7 +113,8 @@
mAmBandConfig = new RadioManager.AmBandConfig.Builder(mAmBandDescriptor).build();
mFmBandConfig = new RadioManager.FmBandConfig.Builder(mFmBandDescriptor).build();
- mRadioTuner = mRadioManager.openTuner(module.getId(), mFmBandConfig, true, mCallback, null);
+ mRadioTuner = mRadioManager.openTuner(module.getId(),
+ mFmBandConfig, withAudio, mCallback, null);
assertNotNull(mRadioTuner);
verify(mCallback, timeout(kConfigCallbacktimeoutNs).times(1)).onConfigurationChanged(any());
verify(mCallback, never()).onError(anyInt());
@@ -178,4 +183,33 @@
verify(mCallback, never()).onError(anyInt());
}
+
+ @Test
+ public void testMute() {
+ openTuner();
+
+ boolean isMuted = mRadioTuner.getMute();
+ assertFalse(isMuted);
+
+ int ret = mRadioTuner.setMute(true);
+ assertEquals(RadioManager.STATUS_OK, ret);
+ isMuted = mRadioTuner.getMute();
+ assertTrue(isMuted);
+
+ ret = mRadioTuner.setMute(false);
+ assertEquals(RadioManager.STATUS_OK, ret);
+ isMuted = mRadioTuner.getMute();
+ assertFalse(isMuted);
+ }
+
+ @Test
+ public void testMuteNoAudio() {
+ openTuner(false);
+
+ int ret = mRadioTuner.setMute(false);
+ assertEquals(RadioManager.STATUS_ERROR, ret);
+
+ boolean isMuted = mRadioTuner.getMute();
+ assertTrue(isMuted);
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
index 8ae212c..aed85a7 100644
--- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -41,6 +41,11 @@
return;
}
+ if (elevation <= 0) {
+ // If elevation is 0, we don't need to paint the shadow
+ return;
+ }
+
Rect originCanvasRect = canvas.getClipBounds();
int saved = modifyCanvas(canvas);
if (saved == -1) {
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index ff632a5..b133a44 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -660,10 +660,10 @@
@Deprecated
public int tx_rate;
- /** average transmit rate. Unit (100kbps). */
+ /** average transmit rate. Unit (kbps). */
public int txRate;
- /** average receiving rate Unit (100kbps). */
+ /** average receiving rate Unit (kbps). */
public int rxRate;
/**
@@ -673,7 +673,7 @@
@Deprecated
public long rtt_ns;
- /** average round trip time in 0.1 nano second. */
+ /** average round trip time in picoseconds. */
public long rtt;
/**
@@ -683,7 +683,7 @@
@Deprecated
public long rtt_sd_ns;
- /** standard deviation of RTT in 0.1 ns. */
+ /** standard deviation of RTT in picoseconds. */
public long rttStandardDeviation;
/**
@@ -693,7 +693,7 @@
@Deprecated
public long rtt_spread_ns;
- /** spread (i.e. max - min) RTT in 0.1 ns. */
+ /** spread (i.e. max - min) RTT in picoseconds. */
public long rttSpread;
/**
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e31a74b..f7333e2 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -839,6 +839,10 @@
*/
public static final int NETWORK_SELECTION_ENABLE = 0;
/**
+ * The starting index for network selection disabled reasons
+ */
+ public static final int NETWORK_SELECTION_DISABLED_STARTING_INDEX = 1;
+ /**
* @deprecated it is not used any more.
* This network is disabled because higher layer (>2) network is bad
*/