diff options
731 files changed, 13203 insertions, 3307 deletions
diff --git a/Android.bp b/Android.bp index df6fdaa5fdf6..6892205e9a8f 100644 --- a/Android.bp +++ b/Android.bp @@ -265,6 +265,60 @@ java_defaults { ], aidl: { generate_get_transaction_name: true, + enforce_permissions: true, + enforce_permissions_exceptions: [ + // Do not add entries to this list. + ":framework-annotations", + ":framework-blobstore-sources", + ":framework-core-sources", + ":framework-drm-sources", + ":framework-graphics-nonupdatable-sources", + ":framework-jobscheduler-sources", + ":framework-keystore-sources", + ":framework-identity-sources", + ":framework-location-sources", + ":framework-lowpan-sources", + ":framework-mca-effect-sources", + ":framework-mca-filterfw-sources", + ":framework-mca-filterpacks-sources", + ":framework-media-non-updatable-sources", + ":framework-mms-sources", + ":framework-omapi-sources", + ":framework-opengl-sources", + ":framework-rs-sources", + ":framework-sax-sources", + ":framework-telecomm-sources", + ":framework-telephony-common-sources", + ":framework-telephony-sources", + ":framework-vcn-util-sources", + ":framework-wifi-annotations", + ":framework-wifi-non-updatable-sources", + ":PacProcessor-aidl-sources", + ":ProxyHandler-aidl-sources", + ":net-utils-framework-common-srcs", + ":platform-compat-native-aidl", + ":credstore_aidl", + ":dumpstate_aidl", + ":framework_native_aidl", + ":gatekeeper_aidl", + ":gsiservice_aidl", + ":idmap2_aidl", + ":idmap2_core_aidl", + ":incidentcompanion_aidl", + ":inputconstants_aidl", + ":installd_aidl", + ":libaudioclient_aidl", + ":libbinder_aidl", + ":libbluetooth-binder-aidl", + ":libcamera_client_aidl", + ":libcamera_client_framework_aidl", + ":libupdate_engine_aidl", + ":logd_aidl", + ":resourcemanager_aidl", + ":storaged_aidl", + ":vold_aidl", + ":deviceproductinfoconstants_aidl", + ], local_include_dirs: [ "media/aidl", ], diff --git a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java index 4cd974141d26..086e18594615 100644 --- a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java +++ b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java @@ -27,6 +27,7 @@ import android.app.Instrumentation; import android.content.Context; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; +import android.util.DisplayMetrics; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; @@ -59,10 +60,13 @@ public class HandwritingInitiatorPerfTest { public void setup() { final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); mContext = mInstrumentation.getTargetContext(); - ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext); + final ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext); mTouchSlop = viewConfiguration.getScaledTouchSlop(); - InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class); - mHandwritingInitiator = new HandwritingInitiator(viewConfiguration, inputMethodManager); + final InputMethodManager inputMethodManager = + mContext.getSystemService(InputMethodManager.class); + final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); + mHandwritingInitiator = new HandwritingInitiator(viewConfiguration, inputMethodManager, + displayMetrics); } @Test diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 968be3e20791..20311587f146 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -71,8 +71,6 @@ static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimat static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip"; static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip"; static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip"; -static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip"; -static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip"; static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip"; static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip"; static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip"; @@ -652,23 +650,6 @@ bool BootAnimation::findBootAnimationFileInternal(const std::vector<std::string> } void BootAnimation::findBootAnimationFile() { - // If the device has encryption turned on or is in process - // of being encrypted we show the encrypted boot animation. - char decrypt[PROPERTY_VALUE_MAX]; - property_get("vold.decrypt", decrypt, ""); - - bool encryptedAnimation = atoi(decrypt) != 0 || - !strcmp("trigger_restart_min_framework", decrypt); - - if (!mShuttingDown && encryptedAnimation) { - static const std::vector<std::string> encryptedBootFiles = { - PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, - }; - if (findBootAnimationFileInternal(encryptedBootFiles)) { - return; - } - } - const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1; static const std::vector<std::string> bootFiles = { APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE, diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md index 64814c8a25e2..988685e23443 100644 --- a/cmds/bootanimation/FORMAT.md +++ b/cmds/bootanimation/FORMAT.md @@ -4,7 +4,6 @@ The system selects a boot animation zipfile from the following locations, in order: - /system/media/bootanimation-encrypted.zip (if getprop("vold.decrypt") = '1') /system/media/bootanimation.zip /oem/media/bootanimation.zip diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 6ef6845c75f2..18ec5a407b24 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -23,6 +23,7 @@ package { cc_defaults { name: "idmap2_defaults", + cpp_std: "gnu++2b", tidy: true, tidy_checks: [ "modernize-*", @@ -31,6 +32,7 @@ cc_defaults { "android-*", "misc-*", "readability-*", + "-readability-identifier-length", ], tidy_checks_as_errors: [ "modernize-*", @@ -54,7 +56,6 @@ cc_defaults { "-readability-convert-member-functions-to-static", "-readability-duplicate-include", "-readability-else-after-return", - "-readability-identifier-length", "-readability-named-parameter", "-readability-redundant-access-specifiers", "-readability-uppercase-literal-suffix", @@ -115,6 +116,7 @@ cc_library { "libidmap2/proto/*.proto", ], host_supported: true, + tidy: false, proto: { type: "lite", export_proto_headers: true, diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp index f2f8854cec3a..f9c4fa3c798b 100644 --- a/cmds/idmap2/tests/ResultTests.cpp +++ b/cmds/idmap2/tests/ResultTests.cpp @@ -259,7 +259,8 @@ TEST(ResultTests, CascadeError) { } struct NoCopyContainer { - uint32_t value; // NOLINT(misc-non-private-member-variables-in-classes) + uint32_t value = 0; // NOLINT(misc-non-private-member-variables-in-classes) + NoCopyContainer() = default; NoCopyContainer(const NoCopyContainer&) = delete; NoCopyContainer& operator=(const NoCopyContainer&) = delete; }; @@ -268,7 +269,7 @@ Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) { if (!succeed) { return Error("foo"); } - std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U}); + std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{}); p->value = 42U; return std::move(p); } diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index d464e266ac36..13c7946a033f 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -54,7 +54,7 @@ public final class Telecom extends BaseCommand { (new Telecom()).run(args); } - + private static final String CALLING_PACKAGE = Telecom.class.getPackageName(); private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled"; private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled"; private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account"; @@ -297,7 +297,7 @@ public final class Telecom extends BaseCommand { final String label = nextArgRequired(); PhoneAccount account = PhoneAccount.builder(handle, label) .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build(); - mTelecomService.registerPhoneAccount(account); + mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE); System.out.println("Success - " + handle + " registered."); } @@ -327,7 +327,7 @@ public final class Telecom extends BaseCommand { .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) .build(); - mTelecomService.registerPhoneAccount(account); + mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE); System.out.println("Success - " + handle + " registered."); } @@ -369,7 +369,7 @@ public final class Telecom extends BaseCommand { private void runUnregisterPhoneAccount() throws RemoteException { final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs(); - mTelecomService.unregisterPhoneAccount(handle); + mTelecomService.unregisterPhoneAccount(handle, CALLING_PACKAGE); System.out.println("Success - " + handle + " unregistered."); } @@ -406,11 +406,11 @@ public final class Telecom extends BaseCommand { } private void runGetDefaultDialer() throws RemoteException { - System.out.println(mTelecomService.getDefaultDialerPackage()); + System.out.println(mTelecomService.getDefaultDialerPackage(CALLING_PACKAGE)); } private void runGetSystemDialer() throws RemoteException { - System.out.println(mTelecomService.getSystemDialerPackage()); + System.out.println(mTelecomService.getSystemDialerPackage(CALLING_PACKAGE)); } private void runWaitOnHandler() throws RemoteException { diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp index 06fa2aac2c7e..3f4163dc54ec 100644 --- a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp +++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp @@ -99,6 +99,7 @@ JNIEnv* DeviceCallback::getJNIEnv() { std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vid, int32_t pid, uint16_t bus, uint32_t ffEffectsMax, + const char* port, std::unique_ptr<DeviceCallback> callback) { android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC)); if (!fd.ok()) { @@ -131,6 +132,9 @@ std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, i return nullptr; } + // set the physical port. + ::ioctl(fd, UI_SET_PHYS, port); + if (::ioctl(fd, UI_DEV_CREATE) != 0) { ALOGE("Unable to create uinput device: %s.", strerror(errno)); return nullptr; @@ -240,17 +244,19 @@ std::vector<int32_t> toVector(JNIEnv* env, jintArray javaArray) { } static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, - jint pid, jint bus, jint ffEffectsMax, jobject callback) { + jint pid, jint bus, jint ffEffectsMax, jstring rawPort, + jobject callback) { ScopedUtfChars name(env, rawName); if (name.c_str() == nullptr) { return 0; } + ScopedUtfChars port(env, rawPort); std::unique_ptr<uinput::DeviceCallback> cb = std::make_unique<uinput::DeviceCallback>(env, callback); std::unique_ptr<uinput::UinputDevice> d = - uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax, + uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax, port.c_str(), std::move(cb)); return reinterpret_cast<jlong>(d.release()); } @@ -303,7 +309,7 @@ static void setAbsInfo(JNIEnv* env, jclass /* clazz */, jint handle, jint axisCo static JNINativeMethod sMethods[] = { {"nativeOpenUinputDevice", - "(Ljava/lang/String;IIIII" + "(Ljava/lang/String;IIIIILjava/lang/String;" "Lcom/android/commands/uinput/Device$DeviceCallback;)J", reinterpret_cast<void*>(openUinputDevice)}, {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)}, diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.h b/cmds/uinput/jni/com_android_commands_uinput_Device.h index 5a9a06cfb32e..6da3d7968ed0 100644 --- a/cmds/uinput/jni/com_android_commands_uinput_Device.h +++ b/cmds/uinput/jni/com_android_commands_uinput_Device.h @@ -48,6 +48,7 @@ class UinputDevice { public: static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vid, int32_t pid, uint16_t bus, uint32_t ff_effects_max, + const char* port, std::unique_ptr<DeviceCallback> callback); virtual ~UinputDevice(); diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java index 62bee7b964bd..732b33d60c15 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Device.java +++ b/cmds/uinput/src/com/android/commands/uinput/Device.java @@ -61,7 +61,7 @@ public class Device { } private static native long nativeOpenUinputDevice(String name, int id, int vid, int pid, - int bus, int ffEffectsMax, DeviceCallback callback); + int bus, int ffEffectsMax, String port, DeviceCallback callback); private static native void nativeCloseUinputDevice(long ptr); private static native void nativeInjectEvent(long ptr, int type, int code, int value); private static native void nativeConfigure(int handle, int code, int[] configs); @@ -69,7 +69,7 @@ public class Device { public Device(int id, String name, int vid, int pid, int bus, SparseArray<int[]> configuration, int ffEffectsMax, - SparseArray<InputAbsInfo> absInfo) { + SparseArray<InputAbsInfo> absInfo, String port) { mId = id; mThread = new HandlerThread("UinputDeviceHandler"); mThread.start(); @@ -88,6 +88,11 @@ public class Device { } else { args.arg1 = id + ":" + vid + ":" + pid; } + if (port != null) { + args.arg2 = port; + } else { + args.arg2 = "uinput:" + id + ":" + vid + ":" + pid; + } mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget(); mTimeToSend = SystemClock.uptimeMillis(); @@ -142,7 +147,7 @@ public class Device { case MSG_OPEN_UINPUT_DEVICE: SomeArgs args = (SomeArgs) msg.obj; mPtr = nativeOpenUinputDevice((String) args.arg1, args.argi1, args.argi2, - args.argi3, args.argi4, args.argi5, + args.argi3, args.argi4, args.argi5, (String) args.arg2, new DeviceCallback()); break; case MSG_INJECT_EVENT: diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java index c4ba05054eda..9add310effaa 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Event.java +++ b/cmds/uinput/src/com/android/commands/uinput/Event.java @@ -64,6 +64,7 @@ public class Event { private SparseArray<int[]> mConfiguration; private int mDuration; private int mFfEffectsMax = 0; + private String mInputport; private SparseArray<InputAbsInfo> mAbsInfo; public int getId() { @@ -110,6 +111,10 @@ public class Event { return mAbsInfo; } + public String getPort() { + return mInputport; + } + /** * Convert an event to String. */ @@ -124,6 +129,7 @@ public class Event { + ", configuration=" + mConfiguration + ", duration=" + mDuration + ", ff_effects_max=" + mFfEffectsMax + + ", port=" + mInputport + "}"; } @@ -178,6 +184,10 @@ public class Event { mEvent.mAbsInfo = absInfo; } + public void setInputport(String port) { + mEvent.mInputport = port; + } + public Event build() { if (mEvent.mId == -1) { throw new IllegalStateException("No event id"); @@ -262,6 +272,9 @@ public class Event { case "duration": eb.setDuration(readInt()); break; + case "port": + eb.setInputport(mReader.nextString()); + break; default: mReader.skipValue(); } diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java index f7601a2f7c07..740578e878ac 100644 --- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java +++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java @@ -123,7 +123,7 @@ public class Uinput { } int id = e.getId(); Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(), - e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo()); + e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo(), e.getPort()); mDevices.append(id, d); } diff --git a/core/api/current.txt b/core/api/current.txt index e260ad046918..b2c6385248ae 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -8996,6 +8996,7 @@ package android.companion { public final class CompanionDeviceManager { method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.Callback); + method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException; method @Deprecated public void disassociate(@NonNull String); method public void disassociate(int); method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations(); @@ -9003,6 +9004,7 @@ package android.companion { method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName); method public void requestNotificationAccess(android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException; + method public void startSystemDataTransfer(int) throws android.companion.DeviceNotAssociatedException; method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException; field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION"; field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE"; @@ -9018,11 +9020,13 @@ package android.companion { public abstract class CompanionDeviceService extends android.app.Service { ctor public CompanionDeviceService(); + method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void dispatchMessageToSystem(int, int, @NonNull byte[]) throws android.companion.DeviceNotAssociatedException; method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String); method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo); method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String); method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo); + method public void onMessageDispatchedFromSystem(int, int, @NonNull byte[]); field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService"; } @@ -18526,7 +18530,7 @@ package android.hardware.fingerprint { package android.hardware.input { public final class InputManager { - method public android.view.InputDevice getInputDevice(int); + method @Nullable public android.view.InputDevice getInputDevice(int); method public int[] getInputDeviceIds(); method @FloatRange(from=0, to=1) public float getMaximumObscuringOpacityForTouch(); method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler); @@ -20081,14 +20085,14 @@ package android.media { method @Deprecated public int getVibrateSetting(int); method @Deprecated public boolean isBluetoothA2dpOn(); method public boolean isBluetoothScoAvailableOffCall(); - method public boolean isBluetoothScoOn(); + method @Deprecated public boolean isBluetoothScoOn(); method public boolean isCallScreeningModeSupported(); method public static boolean isHapticPlaybackSupported(); method public boolean isMicrophoneMute(); method public boolean isMusicActive(); method public static boolean isOffloadedPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes); method public boolean isRampingRingerEnabled(); - method public boolean isSpeakerphoneOn(); + method @Deprecated public boolean isSpeakerphoneOn(); method public boolean isStreamMute(int); method public boolean isSurroundFormatEnabled(int); method public boolean isVolumeFixed(); @@ -20117,7 +20121,7 @@ package android.media { method public void setParameters(String); method public void setRingerMode(int); method @Deprecated public void setRouting(int, int, int); - method public void setSpeakerphoneOn(boolean); + method @Deprecated public void setSpeakerphoneOn(boolean); method @Deprecated public void setStreamMute(int, boolean); method @Deprecated public void setStreamSolo(int, boolean); method public void setStreamVolume(int, int, int); @@ -20125,8 +20129,8 @@ package android.media { method @Deprecated public void setVibrateSetting(int, int); method @Deprecated public void setWiredHeadsetOn(boolean); method @Deprecated public boolean shouldVibrate(int); - method public void startBluetoothSco(); - method public void stopBluetoothSco(); + method @Deprecated public void startBluetoothSco(); + method @Deprecated public void stopBluetoothSco(); method public void unloadSoundEffects(); method public void unregisterAudioDeviceCallback(android.media.AudioDeviceCallback); method public void unregisterAudioPlaybackCallback(@NonNull android.media.AudioManager.AudioPlaybackCallback); @@ -32341,6 +32345,9 @@ package android.os { method public static android.os.VibrationEffect createWaveform(long[], int[], int); method public int describeContents(); method @NonNull public static android.os.VibrationEffect.Composition startComposition(); + method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(); + method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter); + method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter); field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR; field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff field public static final int EFFECT_CLICK = 0; // 0x0 @@ -32350,10 +32357,13 @@ package android.os { } public static final class VibrationEffect.Composition { + method @NonNull public android.os.VibrationEffect.Composition addEffect(@NonNull android.os.VibrationEffect); + method @NonNull public android.os.VibrationEffect.Composition addOffDuration(@NonNull java.time.Duration); method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int); method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float); method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int); method @NonNull public android.os.VibrationEffect compose(); + method @NonNull public android.os.VibrationEffect.Composition repeatEffectIndefinitely(@NonNull android.os.VibrationEffect); field public static final int PRIMITIVE_CLICK = 1; // 0x1 field public static final int PRIMITIVE_LOW_TICK = 8; // 0x8 field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6 @@ -32364,15 +32374,34 @@ package android.os { field public static final int PRIMITIVE_TICK = 7; // 0x7 } + public static final class VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException extends java.lang.IllegalStateException { + } + + public static class VibrationEffect.VibrationParameter { + method @NonNull public static android.os.VibrationEffect.VibrationParameter targetAmplitude(@FloatRange(from=0, to=1) float); + method @NonNull public static android.os.VibrationEffect.VibrationParameter targetFrequency(@FloatRange(from=1) float); + } + + public static final class VibrationEffect.WaveformBuilder { + method @NonNull public android.os.VibrationEffect.WaveformBuilder addSustain(@NonNull java.time.Duration); + method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter); + method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter); + method @NonNull public android.os.VibrationEffect build(); + } + public abstract class Vibrator { method public final int areAllEffectsSupported(@NonNull int...); method public final boolean areAllPrimitivesSupported(@NonNull int...); method @NonNull public int[] areEffectsSupported(@NonNull int...); method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...); method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel(); + method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile(); method public int getId(); method @NonNull public int[] getPrimitiveDurations(@NonNull int...); + method public float getQFactor(); + method public float getResonantFrequency(); method public abstract boolean hasAmplitudeControl(); + method public boolean hasFrequencyControl(); method public abstract boolean hasVibrator(); method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long); method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long, android.media.AudioAttributes); @@ -32700,6 +32729,17 @@ package android.os.strictmode { } +package android.os.vibrator { + + public final class VibratorFrequencyProfile { + method public float getMaxAmplitudeMeasurementInterval(); + method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements(); + method public float getMaxFrequency(); + method public float getMinFrequency(); + } + +} + package android.preference { @Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference { @@ -44254,6 +44294,9 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; + field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0 + field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1 + field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 } public final class ImsReasonInfo implements android.os.Parcelable { @@ -44475,10 +44518,10 @@ package android.telephony.ims { method public void unregisterFeatureProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback); } - public static class ProvisioningManager.FeatureProvisioningCallback { + public abstract static class ProvisioningManager.FeatureProvisioningCallback { ctor public ProvisioningManager.FeatureProvisioningCallback(); - method public void onFeatureProvisioningChanged(int, int, boolean); - method public void onRcsFeatureProvisioningChanged(int, int, boolean); + method public abstract void onFeatureProvisioningChanged(int, int, boolean); + method public abstract void onRcsFeatureProvisioningChanged(int, int, boolean); } public class RcsUceAdapter { @@ -44521,15 +44564,6 @@ package android.telephony.ims.feature { field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1 } - public class RcsFeature { - } - - public static class RcsFeature.RcsImsCapabilities { - field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0 - field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1 - field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 - } - } package android.telephony.ims.stub { @@ -48132,7 +48166,7 @@ package android.view { method @NonNull public android.hardware.BatteryState getBatteryState(); method public int getControllerNumber(); method public String getDescriptor(); - method public static android.view.InputDevice getDevice(int); + method @Nullable public static android.view.InputDevice getDevice(int); method public static int[] getDeviceIds(); method public int getId(); method public android.view.KeyCharacterMap getKeyCharacterMap(); @@ -52511,6 +52545,7 @@ package android.view.autofill { public final class AutofillManager { method public void cancel(); + method public void clearAutofillRequestCallback(); method public void commit(); method public void disableAutofillServices(); method @Nullable public android.content.ComponentName getAutofillServiceComponentName(); @@ -52536,6 +52571,7 @@ package android.view.autofill { method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback); method public void requestAutofill(@NonNull android.view.View); method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect); + method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback); method public void setUserData(@Nullable android.service.autofill.UserData); method public boolean showAutofillDialog(@NonNull android.view.View); method public boolean showAutofillDialog(@NonNull android.view.View, int); @@ -52556,6 +52592,10 @@ package android.view.autofill { field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3 } + public interface AutofillRequestCallback { + method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback); + } + public final class AutofillValue implements android.os.Parcelable { method public int describeContents(); method public static android.view.autofill.AutofillValue forDate(long); @@ -52943,10 +52983,12 @@ package android.view.inputmethod { ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build(); + method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int); + method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList); } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index b9522163b5d0..e81d7f1a9fce 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -2,6 +2,7 @@ package android { public static final class Manifest.permission { + field public static final String BLUETOOTH_STACK = "android.permission.BLUETOOTH_STACK"; field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS"; field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT"; field public static final String MAKE_UID_VISIBLE = "android.permission.MAKE_UID_VISIBLE"; @@ -156,12 +157,12 @@ package android.media { method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp(); method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio(); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpSamplingRate(int); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpVolume(int); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setA2dpSuspended(boolean); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setHfpEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setHfpSamplingRate(int); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setHfpVolume(int); method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index b25d1e302da5..356998527801 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -199,6 +199,7 @@ package android { field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN"; field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE"; field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED"; + field public static final String MEDIA_RESOURCE_OVERRIDE_PID = "android.permission.MEDIA_RESOURCE_OVERRIDE_PID"; field public static final String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"; field public static final String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING"; field public static final String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS"; @@ -1050,7 +1051,7 @@ package android.app { public class WallpaperManager { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int); - method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount(); + method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount(); method public void setDisplayOffset(android.os.IBinder, int, int); method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float); @@ -6334,7 +6335,7 @@ package android.media { } public final class MediaCodec { - method @NonNull @RequiresPermission("android.permission.MEDIA_RESOURCE_OVERRIDE_PID") public static android.media.MediaCodec createByCodecNameForClient(@NonNull String, int, int) throws java.io.IOException; + method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_RESOURCE_OVERRIDE_PID) public static android.media.MediaCodec createByCodecNameForClient(@NonNull String, int, int) throws java.io.IOException; } public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { @@ -11873,19 +11874,20 @@ package android.service.trust { package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { - method @Nullable public android.content.Intent createEnrollIntent(); - method @Nullable public android.content.Intent createReEnrollIntent(); - method @Nullable public android.content.Intent createUnEnrollIntent(); - method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int); + method @Nullable public android.content.Intent createEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @Nullable public android.content.Intent createReEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @Nullable public android.content.Intent createUnEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; method public int getSupportedAudioCapabilities(); - method public int getSupportedRecognitionModes(); - method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int); - method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int); - method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int); - method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(); - method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle); - method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition(); - method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory); + method public int getSupportedRecognitionModes() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int, @NonNull byte[]) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1 field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2 field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0 @@ -11992,10 +11994,10 @@ package android.service.voice { public interface HotwordDetector { method public default void destroy(); - method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(); - method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle); - method public boolean stopRecognition(); - method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory); + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException; + method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException; } public static interface HotwordDetector.Callback { @@ -12008,6 +12010,9 @@ package android.service.voice { method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult); } + public static class HotwordDetector.IllegalDetectorStateException extends android.util.AndroidException { + } + public final class HotwordRejectedResult implements android.os.Parcelable { method public int describeContents(); method public int getConfidenceLevel(); @@ -15024,7 +15029,7 @@ package android.telephony.ims { method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; - field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 + field @Deprecated public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6 @@ -15307,6 +15312,9 @@ package android.telephony.ims.feature { method public void addCapabilities(int); method public boolean isCapable(int); method public void removeCapabilities(int); + field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0 + field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1 + field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 } } diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 1b45e88584fe..db375d42f0b1 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -99,15 +99,6 @@ ProtectedMember: android.service.notification.NotificationAssistantService#attac -RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimAmount(): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) -RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimmingAmount(): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) -RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimAmount(float): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) -RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimmingAmount(float): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) - SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f4a12a5a6a0f..05fe6e848dfb 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -36,7 +36,6 @@ package android { field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; - field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"; field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"; @@ -1250,10 +1249,12 @@ package android.hardware.input { } public final class InputManager { + method public void addUniqueIdAssociation(@NonNull String, @NonNull String); method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context); method @Nullable public String getCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier); method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice); method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String); + method public void removeUniqueIdAssociation(@NonNull String); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int); method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@FloatRange(from=0, to=1) float); @@ -1857,7 +1858,6 @@ package android.os { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle); - method public static boolean isGuestUserEphemeral(); method public static boolean isSplitSystemUser(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException; } @@ -1871,9 +1871,6 @@ package android.os { method public static android.os.VibrationEffect get(int, boolean); method @Nullable public static android.os.VibrationEffect get(android.net.Uri, android.content.Context); method public abstract long getDuration(); - method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(); - method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter); - method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter); field public static final int EFFECT_POP = 4; // 0x4 field public static final int EFFECT_STRENGTH_LIGHT = 0; // 0x0 field public static final int EFFECT_STRENGTH_MEDIUM = 1; // 0x1 @@ -1891,33 +1888,8 @@ package android.os { field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Composed> CREATOR; } - public static final class VibrationEffect.Composition { - method @NonNull public android.os.VibrationEffect.Composition addEffect(@NonNull android.os.VibrationEffect); - method @NonNull public android.os.VibrationEffect.Composition addOffDuration(@NonNull java.time.Duration); - method @NonNull public android.os.VibrationEffect.Composition repeatEffectIndefinitely(@NonNull android.os.VibrationEffect); - } - - public static final class VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException extends java.lang.IllegalStateException { - } - - public static class VibrationEffect.VibrationParameter { - method @NonNull public static android.os.VibrationEffect.VibrationParameter targetAmplitude(@FloatRange(from=0, to=1) float); - method @NonNull public static android.os.VibrationEffect.VibrationParameter targetFrequency(@FloatRange(from=1) float); - } - - public static final class VibrationEffect.WaveformBuilder { - method @NonNull public android.os.VibrationEffect.WaveformBuilder addSustain(@NonNull java.time.Duration); - method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter); - method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter); - method @NonNull public android.os.VibrationEffect build(); - } - public abstract class Vibrator { method public int getDefaultVibrationIntensity(int); - method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile(); - method public float getQFactor(); - method public float getResonantFrequency(); - method public boolean hasFrequencyControl(); field public static final int VIBRATION_INTENSITY_HIGH = 3; // 0x3 field public static final int VIBRATION_INTENSITY_LOW = 1; // 0x1 field public static final int VIBRATION_INTENSITY_MEDIUM = 2; // 0x2 @@ -2102,13 +2074,6 @@ package android.os.vibrator { field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.VibrationEffectSegment> CREATOR; } - public final class VibratorFrequencyProfile { - method public float getMaxAmplitudeMeasurementInterval(); - method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements(); - method public float getMaxFrequency(); - method public float getMinFrequency(); - } - } package android.permission { @@ -2467,6 +2432,8 @@ package android.service.quicksettings { package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { + method public void overrideAvailability(int); + method public void resetAvailability(); method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], @NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>); } @@ -2826,6 +2793,7 @@ package android.view { method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); method @Nullable public android.view.Display.Mode getSystemPreferredDisplayMode(); method public int getType(); + method @Nullable public String getUniqueId(); method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode(); method public boolean hasAccess(int); method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode); diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index e6cdcc0ee742..0d6a07938e95 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -31,7 +31,6 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; -import java.util.Objects; import java.util.Set; /** @@ -87,12 +86,6 @@ public class Account implements Parcelable { if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("the type must not be empty: " + type); } - if (name.length() > 200) { - throw new IllegalArgumentException("account name is longer than 200 characters"); - } - if (type.length() > 200) { - throw new IllegalArgumentException("account type is longer than 200 characters"); - } this.name = name; this.type = type; this.accessId = accessId; diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 28c273ec50a6..167de463ad17 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -211,6 +211,7 @@ interface IWallpaperManager { * * @hide */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)") oneway void setWallpaperDimAmount(float dimAmount); /** @@ -219,6 +220,7 @@ interface IWallpaperManager { * * @hide */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)") float getWallpaperDimAmount(); /** diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 5d6e2bda0b83..7964ae904392 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -431,8 +431,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { writeTo.write(buffer, 0, readByteCount); writeTo.flush(); } - } catch (IOException ioe) { - Log.w(TAG, "Error while reading/writing to streams"); + } catch (IOException ignored) { } finally { IoUtils.closeQuietly(readFrom); IoUtils.closeQuietly(writeTo); diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 0a18588e0131..ea80369983a5 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -2023,7 +2023,7 @@ public class WallpaperManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) - public float getWallpaperDimAmount() { + public @FloatRange (from = 0f, to = 1f) float getWallpaperDimAmount() { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index d11b23cc871b..26b9bc82cd38 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -148,8 +148,8 @@ import java.util.function.Consumer; * <p><b>Note: </b>on * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}, some methods can * throw an {@link UnsafeStateException} exception (for example, if the vehicle is moving), so - * callers running on automotive builds should wrap every method call under the methods provided by - * {@code android.car.admin.CarDevicePolicyManager}. + * callers running on automotive builds should always check for that exception, otherwise they + * might crash. * * <div class="special reference"> * <h3>Developer Guides</h3> diff --git a/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS b/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS index f5604347065e..0ec825371515 100644 --- a/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS +++ b/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS @@ -1,4 +1,5 @@ rubinxu@google.com acjohnston@google.com pgrafov@google.com +ayushsha@google.com alexkershaw@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file diff --git a/core/java/android/app/admin/WorkDeviceExperience_OWNERS b/core/java/android/app/admin/WorkDeviceExperience_OWNERS index dcacaa25a236..7c90feb1871f 100644 --- a/core/java/android/app/admin/WorkDeviceExperience_OWNERS +++ b/core/java/android/app/admin/WorkDeviceExperience_OWNERS @@ -2,4 +2,5 @@ work-device-experience+reviews@google.com scottjonathan@google.com arangelov@google.com kholoudm@google.com +eliselliott@google.com alexkershaw@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 67f631f98f0b..88a7c0f910d3 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -246,6 +246,9 @@ public class BackupManager { * new changes to its data. A backup operation using your application's * {@link android.app.backup.BackupAgent} subclass will be scheduled when you * call this method. + * + * <p> + * Note: This only works if your application is performing Key/Value backups. */ public void dataChanged() { checkServiceBinder(); @@ -268,6 +271,8 @@ public class BackupManager { * as the caller. * * @param packageName The package name identifying the application to back up. + * <p> + * Note: Only works for packages performing Key/Value backups. */ public static void dataChanged(String packageName) { checkServiceBinder(); diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl index 6f73d02caa12..f31e2bce84b4 100644 --- a/core/java/android/app/slice/ISliceManager.aidl +++ b/core/java/android/app/slice/ISliceManager.aidl @@ -33,7 +33,7 @@ interface ISliceManager { // Perms. void grantSlicePermission(String callingPkg, String toPkg, in Uri uri); void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri); - int checkSlicePermission(in Uri uri, String callingPkg, String pkg, int pid, int uid, + int checkSlicePermission(in Uri uri, String callingPkg, int pid, int uid, in String[] autoGrantPermissions); void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); } diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index 5497b78c3f81..ed4ea749c181 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -439,8 +439,8 @@ public class SliceManager { */ public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) { try { - return mService.checkSlicePermission(uri, mContext.getPackageName(), null, pid, uid, - null); + return mService.checkSlicePermission(uri, mContext.getPackageName(), pid, uid, + null /* autoGrantPermissions */); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -488,17 +488,13 @@ public class SliceManager { * Does the permission check to see if a caller has access to a specific slice. * @hide */ - public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid, - String[] autoGrantPermissions) { + public void enforceSlicePermission(Uri uri, int pid, int uid, String[] autoGrantPermissions) { try { if (UserHandle.isSameApp(uid, Process.myUid())) { return; } - if (pkg == null) { - throw new SecurityException("No pkg specified"); - } - int result = mService.checkSlicePermission(uri, mContext.getPackageName(), pkg, pid, - uid, autoGrantPermissions); + int result = mService.checkSlicePermission(uri, mContext.getPackageName(), pid, uid, + autoGrantPermissions); if (result == PERMISSION_DENIED) { throw new SecurityException("User " + uid + " does not have slice permission for " + uri + "."); diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index fb8a83bfc0ef..e6c88a31a1b2 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -452,8 +452,8 @@ public abstract class SliceProvider extends ContentProvider { String pkg = callingPkg != null ? callingPkg : getContext().getPackageManager().getNameForUid(callingUid); try { - mSliceManager.enforceSlicePermission(sliceUri, pkg, - callingPid, callingUid, mAutoGrantPermissions); + mSliceManager.enforceSlicePermission(sliceUri, callingPid, callingUid, + mAutoGrantPermissions); } catch (SecurityException e) { return createPermissionSlice(getContext(), sliceUri, pkg); } diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 257530b26eec..75ab11531595 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -151,7 +151,7 @@ public final class AssociationRequest implements Parcelable { * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for * "self-managed" association. */ - private final @Nullable CharSequence mDisplayName; + private @Nullable CharSequence mDisplayName; /** * Whether the association is to be managed by the companion application. @@ -302,6 +302,11 @@ public final class AssociationRequest implements Parcelable { } /** @hide */ + public void setDisplayName(CharSequence displayName) { + mDisplayName = displayName; + } + + /** @hide */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public List<DeviceFilter<?>> getDeviceFilters() { @@ -597,7 +602,9 @@ public final class AssociationRequest implements Parcelable { boolean forceConfirmation = (flg & 0x20) != 0; boolean skipPrompt = (flg & 0x400) != 0; List<DeviceFilter<?>> deviceFilters = new ArrayList<>(); - in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), (Class<android.companion.DeviceFilter<?>>) (Class<?>) android.companion.DeviceFilter.class); + in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), + (Class<android.companion.DeviceFilter<?>>) (Class<?>) + android.companion.DeviceFilter.class); String deviceProfile = (flg & 0x4) == 0 ? null : in.readString(); CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence(); String packageName = (flg & 0x40) == 0 ? null : in.readString(); @@ -641,10 +648,10 @@ public final class AssociationRequest implements Parcelable { }; @DataClass.Generated( - time = 1643238443303L, + time = 1649179640045L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java", - inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate final @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)") + inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic void setDisplayName(java.lang.CharSequence)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 56939f0ae650..10ab034c85a1 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -774,7 +774,8 @@ public final class CompanionDeviceManager { } /** - * Dispatch a message to system for processing. + * Dispatch a message to system for processing. It should only be called by + * {@link CompanionDeviceService#dispatchMessageToSystem(int, int, byte[])} * * <p>Calling app must declare uses-permission * {@link android.Manifest.permission#DELIVER_COMPANION_MESSAGES}</p> @@ -874,6 +875,66 @@ public final class CompanionDeviceManager { } } + /** + * Build a permission sync user consent dialog. + * + * <p>Only the companion app which owns the association can call this method. Otherwise a null + * IntentSender will be returned from this method and an error will be logged. + * The The app should launch the {@link Activity} in the returned {@code intentSender} + * {@link IntentSender} by calling + * {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}.</p> + * + * <p>The permission transfer doesn't happen immediately after the call or user consented. + * The app needs to trigger the system data transfer manually by calling + * {@link #startSystemDataTransfer(int)}, when it confirms the communication channel between + * the two devices is established.</p> + * + * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the association + * of the companion device recorded by CompanionDeviceManager + * @return An {@link IntentSender} that the app should use to launch the UI for + * the user to confirm the system data transfer request. + */ + @UserHandleAware + @Nullable + public IntentSender buildPermissionTransferUserConsentIntent(int associationId) + throws DeviceNotAssociatedException { + try { + PendingIntent pendingIntent = mService.buildPermissionTransferUserConsentIntent( + mContext.getOpPackageName(), + mContext.getUserId(), + associationId); + if (pendingIntent == null) { + return null; + } + return pendingIntent.getIntentSender(); + } catch (RemoteException e) { + ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Start system data transfer which has been previously approved by the user. + * + * <p>Before calling this method, the app needs to make sure there's a communication channel + * between two devices, and has prompted user consent dialogs built by one of these methods: + * {@link #buildPermissionTransferUserConsentIntent(int)}. + * The transfer may fail if the communication channel is disconnected during the transfer.</p> + * + * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the Association + * of the companion device recorded by CompanionDeviceManager + * @throws DeviceNotAssociatedException Exception if the companion device is not associated + */ + @UserHandleAware + public void startSystemDataTransfer(int associationId) throws DeviceNotAssociatedException { + try { + mService.startSystemDataTransfer(mContext.getUserId(), associationId); + } catch (RemoteException e) { + ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class); + throw e.rethrowFromSystemServer(); + } + } + private boolean checkFeaturePresent() { boolean featurePresent = mService != null; if (!featurePresent && DEBUG) { diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java index 9e1bf4bb9484..bb2189d554f3 100644 --- a/core/java/android/companion/CompanionDeviceService.java +++ b/core/java/android/companion/CompanionDeviceService.java @@ -29,6 +29,7 @@ import android.os.IBinder; import android.util.Log; import java.util.Objects; +import java.util.concurrent.Executor; /** * A service that receives calls from the system when the associated companion device appears @@ -152,11 +153,9 @@ public abstract class CompanionDeviceService extends Service { * @param messageId system assigned id of the message to be sent * @param associationId association id of the associated device * @param message message to be sent - * - * @hide */ - @MainThread - public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) { + public void onMessageDispatchedFromSystem(int messageId, int associationId, + @NonNull byte[] message) { // do nothing. Companion apps can override this function for system to send messages. } @@ -167,17 +166,31 @@ public abstract class CompanionDeviceService extends Service { * <p>Calling app must declare uses-permission * {@link android.Manifest.permission#DELIVER_COMPANION_MESSAGES}</p> * + * <p>Note 1: messageId was assigned by the system, and sender should send the messageId along + * with the message to the receiver. messageId will later be used for verification purpose. + * Misusing the messageId will result in no action.</p> + * + * <p>Note 2: associationId should be local to your device which is calling this API. It's not + * the associationId on your remote device. If you don't have one, you can call + * {@link CompanionDeviceManager#associate(AssociationRequest, Executor, + * CompanionDeviceManager.Callback)} to create one. Misusing the associationId will result in + * {@link DeviceNotAssociatedException}.</p> + * * @param messageId id of the message * @param associationId id of the associated device - * @param message messaged received from the associated device - * - * @hide + * @param message message received from the associated device */ @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) - public final void dispatchMessage(int messageId, int associationId, @NonNull byte[] message) { + public final void dispatchMessageToSystem(int messageId, int associationId, + @NonNull byte[] message) + throws DeviceNotAssociatedException { CompanionDeviceManager companionDeviceManager = getSystemService(CompanionDeviceManager.class); - companionDeviceManager.dispatchMessage(messageId, associationId, message); + if (companionDeviceManager != null) { + companionDeviceManager.dispatchMessage(messageId, associationId, message); + } else { + Log.e(LOG_TAG, "CompanionDeviceManager is null. Can't dispatch messages."); + } } /** @@ -239,9 +252,11 @@ public abstract class CompanionDeviceService extends Service { } @Override - public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) { + public void onMessageDispatchedFromSystem(int messageId, int associationId, + @NonNull byte[] message) { mMainHandler.postAtFrontOfQueue( - () -> mService.onDispatchMessage(messageId, associationId, message)); + () -> mService.onMessageDispatchedFromSystem(messageId, associationId, + message)); } } } diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl index 68a6031f543f..f5b69387c467 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -62,7 +62,7 @@ interface ICompanionDeviceManager { void createAssociation(in String packageName, in String macAddress, int userId, in byte[] certificate); - void dispatchMessage(in int messageId, in int associationId, in byte[] message); + void dispatchMessage(int messageId, int associationId, in byte[] message); void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId); @@ -71,4 +71,9 @@ interface ICompanionDeviceManager { void notifyDeviceAppeared(int associationId); void notifyDeviceDisappeared(int associationId); + + PendingIntent buildPermissionTransferUserConsentIntent(String callingPackage, int userId, + int associationId); + + void startSystemDataTransfer(int userId, int associationId); } diff --git a/core/java/android/companion/ICompanionDeviceService.aidl b/core/java/android/companion/ICompanionDeviceService.aidl index 4e453573f62e..3c90b86ca8c0 100644 --- a/core/java/android/companion/ICompanionDeviceService.aidl +++ b/core/java/android/companion/ICompanionDeviceService.aidl @@ -22,5 +22,5 @@ import android.companion.AssociationInfo; oneway interface ICompanionDeviceService { void onDeviceAppeared(in AssociationInfo associationInfo); void onDeviceDisappeared(in AssociationInfo associationInfo); - void onDispatchMessage(in int messageId, in int associationId, in byte[] message); + void onMessageDispatchedFromSystem(in int messageId, in int associationId, in byte[] message); } diff --git a/core/java/android/companion/SystemDataTransferRequest.java b/core/java/android/companion/SystemDataTransferRequest.java deleted file mode 100644 index e3b0369e203d..000000000000 --- a/core/java/android/companion/SystemDataTransferRequest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2022 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.companion; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.provider.OneTimeUseBuilder; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A request for users to allow the companion app to transfer system data to the companion devices. - * - * @hide - */ -public final class SystemDataTransferRequest implements Parcelable { - - private final int mAssociationId; - private final boolean mPermissionSyncAllPackages; - private final List<String> mPermissionSyncPackages; - - /** - * @hide - */ - public SystemDataTransferRequest(int associationId, boolean syncAllPackages, - @Nullable List<String> permissionSyncPackages) { - mAssociationId = associationId; - mPermissionSyncAllPackages = syncAllPackages; - mPermissionSyncPackages = permissionSyncPackages; - } - - public int getAssociationId() { - return mAssociationId; - } - - @NonNull - public boolean isPermissionSyncAllPackages() { - return mPermissionSyncAllPackages; - } - - @NonNull - public List<String> getPermissionSyncPackages() { - return mPermissionSyncPackages; - } - - /** - * A builder for {@link SystemDataTransferRequest}. - * - * <p>You have to call one of the below methods to create a valid request</p> - * <br>1. {@link #setPermissionSyncAllPackages()} - * <br>2. {@link #setPermissionSyncPackages(List)} - */ - public static final class Builder extends OneTimeUseBuilder<SystemDataTransferRequest> { - - private final int mAssociationId; - private boolean mPermissionSyncAllPackages; - private List<String> mPermissionSyncPackages = new ArrayList<>(); - - public Builder(int associationId) { - mAssociationId = associationId; - } - - /** - * Call to sync permissions for all the packages. You can optionally call - * {@link #setPermissionSyncPackages(List)} to specify the packages to sync permissions. - * - * <p>The system will only sync permissions that are explicitly granted by the user.</p> - * - * <p>If a permission is granted or revoked by the system or a policy, even if the user has - * explicitly granted or revoked the permission earlier, the permission will be ignored.</p> - * - * <p>If a system or policy granted or revoked permission is granted or revoked by the user - * later, the permission will be ignored.</p> - * - * @see #setPermissionSyncPackages(List) - * - * @return the builder - */ - @NonNull - public Builder setPermissionSyncAllPackages() { - mPermissionSyncAllPackages = true; - return this; - } - - /** - * Set a list of packages to sync permissions. You can optionally call - * {@link #setPermissionSyncAllPackages()} to sync permissions for all the packages. - * - * @see #setPermissionSyncAllPackages() - * - * @param permissionSyncPackages packages to sync permissions - * @return builder - */ - @NonNull - public Builder setPermissionSyncPackages(@NonNull List<String> permissionSyncPackages) { - mPermissionSyncPackages = permissionSyncPackages; - return this; - } - - @Override - @NonNull - public SystemDataTransferRequest build() { - return new SystemDataTransferRequest(mAssociationId, mPermissionSyncAllPackages, - mPermissionSyncPackages); - } - } - - SystemDataTransferRequest(Parcel in) { - mAssociationId = in.readInt(); - mPermissionSyncAllPackages = in.readBoolean(); - mPermissionSyncPackages = Arrays.asList(in.createString8Array()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mAssociationId); - dest.writeBoolean(mPermissionSyncAllPackages); - dest.writeString8Array(mPermissionSyncPackages.toArray(new String[0])); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - public static final Creator<SystemDataTransferRequest> CREATOR = - new Creator<SystemDataTransferRequest>() { - @Override - public SystemDataTransferRequest createFromParcel(Parcel in) { - return new SystemDataTransferRequest(in); - } - - @Override - public SystemDataTransferRequest[] newArray(int size) { - return new SystemDataTransferRequest[size]; - } - }; -} diff --git a/core/java/android/companion/SystemDataTransferRequest.aidl b/core/java/android/companion/datatransfer/PermissionSyncRequest.aidl index 19ae60effa7a..76a92e33e544 100644 --- a/core/java/android/companion/SystemDataTransferRequest.aidl +++ b/core/java/android/companion/datatransfer/PermissionSyncRequest.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.companion; +package android.companion.datatransfer; -parcelable SystemDataTransferRequest; +parcelable PermissionSyncRequest; diff --git a/core/java/android/companion/datatransfer/PermissionSyncRequest.java b/core/java/android/companion/datatransfer/PermissionSyncRequest.java new file mode 100644 index 000000000000..973fca30c765 --- /dev/null +++ b/core/java/android/companion/datatransfer/PermissionSyncRequest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 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.companion.datatransfer; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The permission sync request class. + * + * @hide + */ +public class PermissionSyncRequest extends SystemDataTransferRequest implements Parcelable { + + /** @hide */ + public PermissionSyncRequest(int associationId) { + super(associationId, DATA_TYPE_PERMISSION_SYNC); + } + + /** @hide */ + @Override + public String toString() { + return "SystemDataTransferRequest(" + + "associationId=" + mAssociationId + + ", userId=" + mUserId + + ", isUserConsented=" + mUserConsented + + ")"; + } + + /** @hide */ + PermissionSyncRequest(Parcel in) { + super(in); + } + + /** @hide */ + @NonNull + public static final Creator<PermissionSyncRequest> CREATOR = + new Creator<PermissionSyncRequest>() { + @Override + public PermissionSyncRequest createFromParcel(Parcel in) { + return new PermissionSyncRequest(in); + } + + @Override + public PermissionSyncRequest[] newArray(int size) { + return new PermissionSyncRequest[size]; + } + }; +} diff --git a/core/java/android/companion/datatransfer/SystemDataTransferRequest.java b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java new file mode 100644 index 000000000000..38a553d8a725 --- /dev/null +++ b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 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.companion.datatransfer; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.os.Parcel; + +/** + * A request for users to allow the companion app to transfer system data to the companion devices. + * + * @hide + */ +public abstract class SystemDataTransferRequest { + + /** @hide */ + public static final int DATA_TYPE_PERMISSION_SYNC = 1; + + final int mAssociationId; + + final int mDataType; + + /** + * User id that the request belongs to. + * Populated by the system. + */ + @UserIdInt + int mUserId; + + /** + * Whether the request is consented by the user. + * Populated by the system + */ + boolean mUserConsented = false; + + /** @hide */ + SystemDataTransferRequest(int associationId, int dataType) { + mAssociationId = associationId; + mDataType = dataType; + } + + /** @hide */ + public int getAssociationId() { + return mAssociationId; + } + + /** @hide */ + public int getDataType() { + return mDataType; + } + + /** @hide */ + public int getUserId() { + return mUserId; + } + + /** @hide */ + public boolean isUserConsented() { + return mUserConsented; + } + + /** @hide */ + public void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** @hide */ + public void setUserConsented(boolean isUserConsented) { + mUserConsented = isUserConsented; + } + + /** @hide */ + SystemDataTransferRequest(Parcel in) { + mAssociationId = in.readInt(); + mDataType = in.readInt(); + mUserId = in.readInt(); + mUserConsented = in.readBoolean(); + } + + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mAssociationId); + dest.writeInt(mDataType); + dest.writeInt(mUserId); + dest.writeBoolean(mUserConsented); + } + + /** @hide */ + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index f4de82946253..970849373fb4 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -448,6 +448,12 @@ public class PackageInfo implements Parcelable { */ public boolean isApex; + /** + * Whether this is an active APEX package. + * @hide + */ + public boolean isActiveApex; + public PackageInfo() { } @@ -534,6 +540,7 @@ public class PackageInfo implements Parcelable { dest.writeInt(0); } dest.writeBoolean(isApex); + dest.writeBoolean(isActiveApex); dest.restoreAllowSquashing(prevAllowSquashing); } @@ -598,5 +605,6 @@ public class PackageInfo implements Parcelable { signingInfo = SigningInfo.CREATOR.createFromParcel(source); } isApex = source.readBoolean(); + isActiveApex = source.readBoolean(); } } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 236c24475844..5f45c342a27c 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -682,7 +682,9 @@ public class PackageInstaller { * </ul> * * @param packageName The package to uninstall. - * @param statusReceiver Where to deliver the result. + * @param statusReceiver Where to deliver the result of the operation indicated by the extra + * {@link #EXTRA_STATUS}. Refer to the individual status codes + * on how to handle them. * * @see android.app.admin.DevicePolicyManager */ @@ -700,7 +702,9 @@ public class PackageInstaller { * * @param packageName The package to uninstall. * @param flags Flags for uninstall. - * @param statusReceiver Where to deliver the result. + * @param statusReceiver Where to deliver the result of the operation indicated by the extra + * {@link #EXTRA_STATUS}. Refer to the individual status codes + * on how to handle them. * * @hide */ @@ -725,7 +729,9 @@ public class PackageInstaller { * </ul> * * @param versionedPackage The versioned package to uninstall. - * @param statusReceiver Where to deliver the result. + * @param statusReceiver Where to deliver the result of the operation indicated by the extra + * {@link #EXTRA_STATUS}. Refer to the individual status codes + * on how to handle them. * * @see android.app.admin.DevicePolicyManager */ @@ -747,7 +753,9 @@ public class PackageInstaller { * * @param versionedPackage The versioned package to uninstall. * @param flags Flags for uninstall. - * @param statusReceiver Where to deliver the result. + * @param statusReceiver Where to deliver the result of the operation indicated by the extra + * {@link #EXTRA_STATUS}. Refer to the individual status codes + * on how to handle them. * * @hide */ @@ -775,7 +783,9 @@ public class PackageInstaller { * * @param packageName The package to install. * @param installReason Reason for install. - * @param statusReceiver Where to deliver the result. + * @param statusReceiver Where to deliver the result of the operation indicated by the extra + * {@link #EXTRA_STATUS}. Refer to the individual status codes + * on how to handle them. */ @RequiresPermission(allOf = { Manifest.permission.INSTALL_PACKAGES, @@ -797,8 +807,10 @@ public class PackageInstaller { * Uninstall the given package for the user for which this installer was created if the package * will still exist for other users on the device. * - * @param packageName The package to install. - * @param statusReceiver Where to deliver the result. + * @param packageName The package to uninstall. + * @param statusReceiver Where to deliver the result of the operation indicated by the extra + * {@link #EXTRA_STATUS}. Refer to the individual status codes + * on how to handle them. */ @RequiresPermission(Manifest.permission.DELETE_PACKAGES) public void uninstallExistingPackage(@NonNull String packageName, @@ -1705,20 +1717,22 @@ public class PackageInstaller { public @interface UserActionRequirement {} /** - * The installer did not call {@link SessionParams#setRequireUserAction(int)} to - * specify whether user action should be required for the install. + * This value is passed by the installer to {@link SessionParams#setRequireUserAction(int)} + * to indicate that user action is unspecified for this install. + * {@code requireUserAction} also defaults to this value unless modified by + * {@link SessionParams#setRequireUserAction(int)} */ public static final int USER_ACTION_UNSPECIFIED = 0; /** - * The installer called {@link SessionParams#setRequireUserAction(int)} with - * {@code true} to require user action for the install to complete. + * This value is passed by the installer to {@link SessionParams#setRequireUserAction(int)} + * to indicate that user action is required for this install. */ public static final int USER_ACTION_REQUIRED = 1; /** - * The installer called {@link SessionParams#setRequireUserAction(int)} with - * {@code false} to request that user action not be required for this install. + * This value is passed by the installer to {@link SessionParams#setRequireUserAction(int)} + * to indicate that user action is not required for this install. */ public static final int USER_ACTION_NOT_REQUIRED = 2; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a90f6d625c51..6f9ec9c780fb 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4247,6 +4247,7 @@ public abstract class PackageManager { */ public static final String EXTRA_VERIFICATION_PACKAGE_NAME = "android.content.pm.extra.VERIFICATION_PACKAGE_NAME"; + /** * Extra field name for the result of a verification, either * {@link #VERIFICATION_ALLOW}, or {@link #VERIFICATION_REJECT}. @@ -4256,6 +4257,14 @@ public abstract class PackageManager { = "android.content.pm.extra.VERIFICATION_RESULT"; /** + * Extra field name for tracking whether user action + * was requested for a particular install, either {@code true} or {@code false}. + * @hide + */ + public static final String EXTRA_USER_ACTION_REQUIRED + = "android.content.pm.extra.USER_ACTION_REQUIRED"; + + /** * Extra field name for the version code of a package pending verification. * @deprecated Use {@link #EXTRA_VERIFICATION_LONG_VERSION_CODE} instead. * @hide @@ -4265,8 +4274,7 @@ public abstract class PackageManager { = "android.content.pm.extra.VERIFICATION_VERSION_CODE"; /** - * Extra field name for the long version code of a package pending verification. - * + * Extra field name for the long version code of a package pending verification * @hide */ public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE = diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 76e9fcb07f22..816460b01529 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -142,6 +142,22 @@ public class UserInfo implements Parcelable { public static final int FLAG_PROFILE = 0x00001000; /** + * Indicates that this user is created in ephemeral mode via + * {@link IUserManager} create user. + * + * When a user is created with {@link #FLAG_EPHEMERAL}, {@link #FLAG_EPHEMERAL_ON_CREATE} + * is set internally within the user manager. + * + * When {@link #FLAG_EPHEMERAL_ON_CREATE} is set {@link IUserManager.setUserEphemeral} + * has no effect because a user that was created ephemeral can never be made non-ephemeral. + * + * {@link #FLAG_EPHEMERAL_ON_CREATE} should NOT be set by client's of user manager + * + * @hide + */ + public static final int FLAG_EPHEMERAL_ON_CREATE = 0x00002000; + + /** * @hide */ @IntDef(flag = true, prefix = "FLAG_", value = { @@ -157,7 +173,8 @@ public class UserInfo implements Parcelable { FLAG_DEMO, FLAG_FULL, FLAG_SYSTEM, - FLAG_PROFILE + FLAG_PROFILE, + FLAG_EPHEMERAL_ON_CREATE }) @Retention(RetentionPolicy.SOURCE) public @interface UserInfoFlag { diff --git a/core/java/android/content/pm/verify/domain/TEST_MAPPING b/core/java/android/content/pm/verify/domain/TEST_MAPPING index ba4a62cdbbf1..8a1982a339ea 100644 --- a/core/java/android/content/pm/verify/domain/TEST_MAPPING +++ b/core/java/android/content/pm/verify/domain/TEST_MAPPING @@ -12,9 +12,6 @@ "name": "CtsDomainVerificationDeviceStandaloneTestCases" }, { - "name": "CtsDomainVerificationDeviceMultiUserTestCases" - }, - { "name": "CtsDomainVerificationHostTestCases" } ] diff --git a/core/java/android/hardware/IConsumerIrService.aidl b/core/java/android/hardware/IConsumerIrService.aidl index c79bd1958226..930c73f0ae44 100644 --- a/core/java/android/hardware/IConsumerIrService.aidl +++ b/core/java/android/hardware/IConsumerIrService.aidl @@ -19,8 +19,13 @@ package android.hardware; /** {@hide} */ interface IConsumerIrService { + @RequiresNoPermission boolean hasIrEmitter(); + + @EnforcePermission("TRANSMIT_IR") void transmit(String packageName, int carrierFrequency, in int[] pattern); + + @EnforcePermission("TRANSMIT_IR") int[] getCarrierFrequencies(); } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index a62bbf623000..36b532f1cbed 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -41,6 +41,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Trace; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -987,7 +988,8 @@ public final class DisplayManagerGlobal { @Override public void onDisplayEvent(int displayId, @DisplayEvent int event) { if (DEBUG) { - Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); + Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + eventToString( + event)); } handleDisplayEvent(displayId, event); } @@ -1021,6 +1023,12 @@ public final class DisplayManagerGlobal { @Override public void handleMessage(Message msg) { + if (DEBUG) { + Trace.beginSection( + "DisplayListenerDelegate(" + eventToString(msg.what) + + ", display=" + msg.arg1 + + ", listener=" + mListener.getClass() + ")"); + } switch (msg.what) { case EVENT_DISPLAY_ADDED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) { @@ -1047,6 +1055,9 @@ public final class DisplayManagerGlobal { } break; } + if (DEBUG) { + Trace.endSection(); + } } } @@ -1150,4 +1161,18 @@ public final class DisplayManagerGlobal { updateCallbackIfNeededLocked(); } } + + private static String eventToString(@DisplayEvent int event) { + switch (event) { + case EVENT_DISPLAY_ADDED: + return "ADDED"; + case EVENT_DISPLAY_CHANGED: + return "CHANGED"; + case EVENT_DISPLAY_REMOVED: + return "REMOVED"; + case EVENT_DISPLAY_BRIGHTNESS_CHANGED: + return "BRIGHTNESS_CHANGED"; + } + return "UNKNOWN"; + } } diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index e1ffd4a6761d..bb8af8fed962 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -38,6 +38,8 @@ import android.view.VerifiedInputEvent; /** @hide */ interface IInputManager { + // Gets the current VelocityTracker strategy + String getVelocityTrackerStrategy(); // Gets input device information. InputDevice getInputDevice(int deviceId); int[] getInputDeviceIds(); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index cc5b275bbf5a..f59b9f7debe1 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -272,8 +272,15 @@ public final class InputManager { */ public static final int SWITCH_STATE_ON = 1; + private static String sVelocityTrackerStrategy; + private InputManager(IInputManager im) { mIm = im; + try { + sVelocityTrackerStrategy = mIm.getVelocityTrackerStrategy(); + } catch (RemoteException ex) { + Log.w(TAG, "Could not get VelocityTracker strategy: " + ex); + } } /** @@ -326,10 +333,19 @@ public final class InputManager { } /** + * Get the current VelocityTracker strategy. Only works when the system has fully booted up. + * @hide + */ + public String getVelocityTrackerStrategy() { + return sVelocityTrackerStrategy; + } + + /** * Gets information about the input device with the specified id. * @param id The device id. * @return The input device or null if not found. */ + @Nullable public InputDevice getInputDevice(int id) { synchronized (mInputDevicesLock) { populateInputDevicesLocked(); @@ -1377,6 +1393,7 @@ public final class InputManager { * </p> * @hide */ + @TestApi public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) { try { mIm.addUniqueIdAssociation(inputPort, displayUniqueId); @@ -1393,6 +1410,7 @@ public final class InputManager { * </p> * @hide */ + @TestApi public void removeUniqueIdAssociation(@NonNull String inputPort) { try { mIm.removeUniqueIdAssociation(inputPort); diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java index 3a6d5087c260..694d6d8c23c0 100644 --- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java +++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java @@ -40,11 +40,11 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -151,7 +151,7 @@ public class KeyphraseEnrollmentInfo { return; } - List<String> parseErrors = new LinkedList<>(); + List<String> parseErrors = new ArrayList<>(); mKeyphrasePackageMap = new HashMap<>(); for (ResolveInfo ri : ris) { try { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6ece5efae537..5f2bd31cffd8 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -147,6 +147,7 @@ import com.android.internal.inputmethod.ImeTracing; import com.android.internal.inputmethod.InputMethodNavButtonFlags; import com.android.internal.inputmethod.InputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.util.RingBuffer; import com.android.internal.view.IInlineSuggestionsRequestCallback; import com.android.internal.view.IInputContext; @@ -1066,7 +1067,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void notifyImeHidden() { - requestHideSelf(0); + requestHideSelf(0 /* flags */, SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API); } private void scheduleImeSurfaceRemoval() { @@ -2967,9 +2968,13 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public void requestHideSelf(int flags) { + requestHideSelf(flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME); + } + + private void requestHideSelf(int flags, @SoftInputShowHideReason int reason) { ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper, null /* icProto */); - mPrivOps.hideMySoftInput(flags); + mPrivOps.hideMySoftInput(flags, reason); } /** @@ -2990,7 +2995,9 @@ public class InputMethodService extends AbstractInputMethodService { if (mShowInputRequested) { // If the soft input area is shown, back closes it and we // consume the back key. - if (doIt) requestHideSelf(0); + if (doIt) { + requestHideSelf(0 /* flags */, SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY); + } return true; } else if (mDecorViewVisible) { if (mCandidatesVisibility == View.VISIBLE) { @@ -3141,7 +3148,8 @@ public class InputMethodService extends AbstractInputMethodService { private void onToggleSoftInput(int showFlags, int hideFlags) { if (DEBUG) Log.v(TAG, "toggleSoftInput()"); if (isInputViewShown()) { - requestHideSelf(hideFlags); + requestHideSelf( + hideFlags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT); } else { requestShowSelf(showFlags); } @@ -3576,7 +3584,8 @@ public class InputMethodService extends AbstractInputMethodService { */ public void onExtractingInputChanged(EditorInfo ei) { if (ei.inputType == InputType.TYPE_NULL) { - requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS); + requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS, + SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED); } } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 815e4f0c9071..d71faee4cc8d 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -1205,13 +1205,16 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } static Uri readFrom(Parcel parcel) { - return new HierarchicalUri( - parcel.readString8(), - Part.readFrom(parcel), - PathPart.readFrom(parcel), - Part.readFrom(parcel), - Part.readFrom(parcel) - ); + final String scheme = parcel.readString8(); + final Part authority = Part.readFrom(parcel); + // In RFC3986 the path should be determined based on whether there is a scheme or + // authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3). + final boolean hasSchemeOrAuthority = + (scheme != null && scheme.length() > 0) || !authority.isEmpty(); + final PathPart path = PathPart.readFrom(hasSchemeOrAuthority, parcel); + final Part query = Part.readFrom(parcel); + final Part fragment = Part.readFrom(parcel); + return new HierarchicalUri(scheme, authority, path, query, fragment); } public int describeContents() { @@ -2270,6 +2273,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } } + static PathPart readFrom(boolean hasSchemeOrAuthority, Parcel parcel) { + final PathPart path = readFrom(parcel); + return hasSchemeOrAuthority ? makeAbsolute(path) : path; + } + /** * Creates a path from the encoded string. * diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 3cde0319efd3..e5de3e157c88 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -131,4 +131,5 @@ interface IUserManager { String getUserName(); long getUserStartRealtime(); long getUserUnlockRealtime(); + boolean setUserEphemeral(int userId, boolean enableEphemeral); } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index e06e7326a860..14082f3388a0 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -213,12 +213,6 @@ public class Process { public static final int SE_UID = 1068; /** - * Defines the UID/GID for the iorapd. - * @hide - */ - public static final int IORAPD_UID = 1071; - - /** * Defines the UID/GID for the NetworkStack app. * @hide */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index a64e63eacd56..570d533ccf1e 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1997,13 +1997,22 @@ public class UserManager { * @return Whether guest user is always ephemeral * @hide */ - @TestApi - public static boolean isGuestUserEphemeral() { + public static boolean isGuestUserAlwaysEphemeral() { return Resources.getSystem() .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); } /** + * @return true, when we want to enable user manager API and UX to allow + * guest user ephemeral state change based on user input + * @hide + */ + public static boolean isGuestUserAllowEphemeralStateChange() { + return Resources.getSystem() + .getBoolean(com.android.internal.R.bool.config_guestUserAllowEphemeralStateChange); + } + + /** * Checks whether the device is running in a headless system user mode. * * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system @@ -3424,6 +3433,20 @@ public class UserManager { if (guest != null) { Settings.Secure.putStringForUser(context.getContentResolver(), Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id); + + if (UserManager.isGuestUserAllowEphemeralStateChange()) { + // Mark guest as (changeably) ephemeral if REMOVE_GUEST_ON_EXIT is 1 + // This is done so that a user via a UI controller can choose to + // make a guest as ephemeral or not. + // Settings.Global.REMOVE_GUEST_ON_EXIT holds the choice on what the guest state + // should be, with default being ephemeral. + boolean resetGuestOnExit = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.REMOVE_GUEST_ON_EXIT, 1) == 1; + + if (resetGuestOnExit && !guest.isEphemeral()) { + setUserEphemeral(guest.id, true); + } + } } return guest; } catch (ServiceSpecificException e) { @@ -4941,6 +4964,31 @@ public class UserManager { } /** + * Set the user as ephemeral or non-ephemeral. + * + * If the user was initially created as ephemeral then this + * method has no effect and false is returned. + * + * @param userId the user's integer id + * @param enableEphemeral true: change user state to ephemeral, + * false: change user state to non-ephemeral + * @return true: user now has the desired ephemeral state, + * false: desired user ephemeral state could not be set + * + * @hide + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS}) + public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) { + try { + return mService.setUserEphemeral(userId, enableEphemeral); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Updates the context user's name. * * @param name the new name for the user diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index ec1c57d675c1..237f6ed819f6 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -413,10 +413,8 @@ public abstract class VibrationEffect implements Parcelable { * {@link #startWaveform(VibrationEffect.VibrationParameter)}. * * @see VibrationEffect.WaveformBuilder - * @hide */ @NonNull - @TestApi public static WaveformBuilder startWaveform() { return new WaveformBuilder(); } @@ -433,10 +431,8 @@ public abstract class VibrationEffect implements Parcelable { * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters. * * @see VibrationEffect.WaveformBuilder - * @hide */ @NonNull - @TestApi public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter) { WaveformBuilder builder = startWaveform(); builder.addTransition(Duration.ZERO, initialParameter); @@ -458,10 +454,8 @@ public abstract class VibrationEffect implements Parcelable { * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters. * * @see VibrationEffect.WaveformBuilder - * @hide */ @NonNull - @TestApi public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter1, @NonNull VibrationParameter initialParameter2) { WaveformBuilder builder = startWaveform(); @@ -875,9 +869,7 @@ public abstract class VibrationEffect implements Parcelable { /** * Exception thrown when adding an element to a {@link Composition} that already ends in an * indefinitely repeating effect. - * @hide */ - @TestApi public static final class UnreachableAfterRepeatingIndefinitelyException extends IllegalStateException { UnreachableAfterRepeatingIndefinitelyException() { @@ -946,10 +938,8 @@ public abstract class VibrationEffect implements Parcelable { * * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently * ending with a repeating effect. - * @hide */ @NonNull - @TestApi public Composition addOffDuration(@NonNull Duration duration) { int durationMs = (int) duration.toMillis(); Preconditions.checkArgumentNonnegative(durationMs, "Off period must be non-negative"); @@ -975,10 +965,8 @@ public abstract class VibrationEffect implements Parcelable { * * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently * ending with a repeating effect. - * @hide */ @NonNull - @TestApi public Composition addEffect(@NonNull VibrationEffect effect) { return addSegments(effect); } @@ -999,10 +987,8 @@ public abstract class VibrationEffect implements Parcelable { * @throws IllegalArgumentException if the given effect is already repeating indefinitely. * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently * ending with a repeating effect. - * @hide */ @NonNull - @TestApi public Composition repeatEffectIndefinitely(@NonNull VibrationEffect effect) { Preconditions.checkArgument(effect.getDuration() < Long.MAX_VALUE, "Can't repeat an indefinitely repeating effect. Consider addEffect instead."); @@ -1216,9 +1202,7 @@ public abstract class VibrationEffect implements Parcelable { * .build();}</pre> * * @see VibrationEffect#startWaveform - * @hide */ - @TestApi public static final class WaveformBuilder { // Epsilon used for float comparison of amplitude and frequency values on transitions. private static final float EPSILON = 1e-5f; @@ -1399,10 +1383,8 @@ public abstract class VibrationEffect implements Parcelable { * <p>Examples of concrete parameters are the vibration amplitude or frequency. * * @see VibrationEffect.WaveformBuilder - * @hide */ @SuppressWarnings("UserHandleName") // This is not a regular set of parameters, no *Params. - @TestApi public static class VibrationParameter { VibrationParameter() { } diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 465d90d36698..7f0d6349f57f 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -212,9 +212,7 @@ public abstract class Vibrator { * * @return True if the hardware can control the frequency of the vibrations independently of * the vibration amplitude, false otherwise. - * @hide */ - @TestApi public boolean hasFrequencyControl() { // We currently can only control frequency of the vibration using the compose PWLE method. return getInfo().hasCapability( @@ -238,9 +236,7 @@ public abstract class Vibrator { * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown, not * applicable, or if this vibrator is a composite of multiple physical devices with different * frequencies. - * @hide */ - @TestApi public float getResonantFrequency() { return getInfo().getResonantFrequencyHz(); } @@ -251,9 +247,7 @@ public abstract class Vibrator { * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown, not * applicable, or if this vibrator is a composite of multiple physical devices with different * Q factors. - * @hide */ - @TestApi public float getQFactor() { return getInfo().getQFactor(); } @@ -268,9 +262,7 @@ public abstract class Vibrator { * frequency control. If this vibrator is a composite of multiple physical devices then this * will return a profile supported in all devices, or null if the intersection is empty or not * available. - * @hide */ - @TestApi @Nullable public VibratorFrequencyProfile getFrequencyProfile() { VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile(); diff --git a/core/java/android/os/vibrator/VibratorFrequencyProfile.java b/core/java/android/os/vibrator/VibratorFrequencyProfile.java index afc0007afd42..0f2aa157d94c 100644 --- a/core/java/android/os/vibrator/VibratorFrequencyProfile.java +++ b/core/java/android/os/vibrator/VibratorFrequencyProfile.java @@ -18,7 +18,6 @@ package android.os.vibrator; import android.annotation.FloatRange; import android.annotation.NonNull; -import android.annotation.TestApi; import android.os.VibratorInfo; import com.android.internal.util.Preconditions; @@ -39,9 +38,7 @@ import com.android.internal.util.Preconditions; * frequency increment between each pair of amplitude values. * * <p>Vibrators without independent frequency control do not have a frequency profile. - * @hide */ -@TestApi public final class VibratorFrequencyProfile { private final VibratorInfo.FrequencyProfile mFrequencyProfile; diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md index dfe748b09464..e61ecd8781d0 100644 --- a/core/java/android/permission/Permissions.md +++ b/core/java/android/permission/Permissions.md @@ -71,9 +71,9 @@ Any app can request any permission via adding an entry in the manifest file like A requested permission does not necessarily mean that the permission is granted. When and how a permission is granted depends on the protection level of the permission. If no protection level is -set, the permission will always be granted. Such "normal" permissions can still be useful as it -will be easy to find apps using a certain functionality on app stores and by checking `dumpsys -package`. +set, it will default to `normal` and the permission will always be granted if requested. Such +`normal` permissions can still be useful as it will be easy to find apps using a certain +functionality on app stores and by checking `dumpsys package`. #### Checking a permission @@ -686,17 +686,37 @@ able to call APIs not available to other apps. This is implemented by granting p these system apps and then enforcing the permissions in the API similar to other [install time permissions](#checking-a-permission). -System apps are not different from regular apps, but the protection levels (e.g. +System apps are not different from regular apps, but the protection flags (e.g. [privileged](#privileged-permissions), [preinstalled](#preinstalled-permissions)) mentioned in this section are more commonly used by system apps. -### Multiple permission levels +### Permission protection level -It is possible to assign multiple protection levels to a permission. Very common combinations are -for example adding `signature` to all permissions to make sure the platform signed apps can be -granted the permission, e.g. `privileged|signature`. +Every permission has a protection level (`android:protectionlevel`), which is a combination of one +required protection (`PermissionInfo.getProtection()`) and multiple optional protection flags +(`PermissionInfo.getProtectionFlags()`). -The permission will be granted if the app qualifies for _any_ of the permission levels. +The protection can be one of the following: + +- [`normal`](#requesting-a-permission): The permission will be granted to apps requesting it in +their manifest. +- [`dangerous`](#runtime-permissions): The permission will be a runtime permission. +- [`signature`](#signature-permissions): The permission will be granted to apps being signed with +the same certificate as the app defining the permission. If the permission is a platform permission, +it means those apps need to be platform-signed. +- `internal`: This is a no-op protection so that it won't allow granting the permission by itself. +However, it will be useful when defining permissions that should only be granted according to its +protection flags, e.g. `internal|role` for a role-only permission. + +There are various optional protection flags that can be added to protection level, in addition to +the required protection, e.g. [appop](#app_op-permissions), +[preinstalled](#preinstalled-permissions), [privileged](#privileged-permissions), +[installer](#limited-permissions), [role](#role-protected-permissions) and +[development](#development-permissions). + +The permission will be granted to an app if it meets _any_ of the protection or protection flags (an +`OR` relationship). For example, `signature|privileged` allows the permission to be granted to +platform-signed apps as well as privileged apps. ### App-op permissions @@ -716,18 +736,15 @@ and special behavior. Hence this section is a guideline, not a rule. #### Defining an app-op permission Only the platform can reasonably define an app-op permission. The permission is defined in the -platforms manifest using the `appop` protection level +platforms manifest using the `appop` protection flag: ```xml <manifest package="android"> <permission android:name="android.permission.MY_APPOP_PERMISSION" - android:protectionLevel="appop|signature" /> + android:protectionLevel="signature|appop" /> </manifest> ``` -Almost always the protection level is app-op | something else, like -[signature](#signature-permissions) (in the case above) or [privileged](#privileged-permissions). - #### Checking an app-op permission The `PermissionChecker` utility can check app-op permissions with the [same syntax as runtime @@ -913,12 +930,12 @@ See > Not recommended -By adding the `development` protection level to any permissions the permission can be granted via +By adding the `development` protection flag to any permissions the permission can be granted via the `pm grant` shell command. This appears to be useful for development and testing, but it is very highly discouraged. Any user can grant them permanently via adb, hence adding this tag removes all guarantees the permission might otherwise provide. -### Other protection levels +### Other protection flags There are other levels (such as `runtime`) but they are for special purposes on should not be used by platform developers. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 111c6704a57d..f790195f3b41 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9659,6 +9659,13 @@ public final class Settings { "biometric_debug_enabled"; /** + * Whether or not virtual sensors are enabled. + * @hide + */ + @Readable + public static final String BIOMETRIC_VIRTUAL_ENABLED = "biometric_virtual_enabled"; + + /** * Whether or not biometric is allowed on Keyguard. * @hide */ @@ -10898,6 +10905,14 @@ public final class Settings { public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked"; /** + * Whether guest user should be removed on exit from guest mode. + * <p> + * Type: int + * @hide + */ + public static final String REMOVE_GUEST_ON_EXIT = "remove_guest_on_exit"; + + /** * Whether applying ramping ringer on incoming phone call ringtone. * <p>1 = apply ramping ringer * <p>0 = do not apply ramping ringer @@ -17252,6 +17267,12 @@ public final class Settings { public static final String AMBIENT_TILT_TO_BRIGHT = "ambient_tilt_to_bright"; /** + * Whether touch and hold to edit WF is enabled + * @hide + */ + public static final String TOUCH_AND_HOLD_WATCH_FACE = "touch_and_hold_watchface"; + + /** * Whether the current watchface is decomposable. * @hide */ @@ -17469,6 +17490,18 @@ public final class Settings { * @hide */ public static final String WET_MODE_ON = "wet_mode_on"; + + /* + * Whether the screen-unlock (keyguard) sound is enabled. + * @hide + */ + public static final String SCREEN_UNLOCK_SOUND_ENABLED = "screen_unlock_sound_enabled"; + + /* + * Whether charging sounds are enabled. + * @hide + */ + public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled"; } } diff --git a/core/java/android/security/attestationverification/AttestationVerificationManager.java b/core/java/android/security/attestationverification/AttestationVerificationManager.java index db783ceabcdb..2e61db1b932a 100644 --- a/core/java/android/security/attestationverification/AttestationVerificationManager.java +++ b/core/java/android/security/attestationverification/AttestationVerificationManager.java @@ -226,10 +226,10 @@ public class AttestationVerificationManager { public static final int PROFILE_SELF_TRUSTED = 2; /** - * A system-defined profile which verifies that the attesting environment environment is similar - * to the current device in terms of security model and security configuration. This category is - * fairly broad and most securely configured Android devices should qualify, along with a - * variety of non-Android devices. + * A system-defined profile which verifies that the attesting environment is similar to the + * current device in terms of security model and security configuration. This category is fairly + * broad and most securely configured Android devices should qualify, along with a variety of + * non-Android devices. */ public static final int PROFILE_PEER_DEVICE = 3; @@ -321,4 +321,52 @@ public class AttestationVerificationManager { /** Requirements bundle parameter for a challenge. */ public static final String PARAM_CHALLENGE = "localbinding.challenge"; + + /** @hide */ + public static String localBindingTypeToString(@LocalBindingType int localBindingType) { + final String text; + switch (localBindingType) { + case TYPE_UNKNOWN: + text = "UNKNOWN"; + break; + + case TYPE_APP_DEFINED: + text = "APP_DEFINED"; + break; + + case TYPE_PUBLIC_KEY: + text = "PUBLIC_KEY"; + break; + + case TYPE_CHALLENGE: + text = "CHALLENGE"; + break; + + default: + return Integer.toString(localBindingType); + } + return text + "(" + localBindingType + ")"; + } + + /** @hide */ + public static String verificationResultCodeToString(@VerificationResult int resultCode) { + final String text; + switch (resultCode) { + case RESULT_UNKNOWN: + text = "UNKNOWN"; + break; + + case RESULT_SUCCESS: + text = "SUCCESS"; + break; + + case RESULT_FAILURE: + text = "FAILURE"; + break; + + default: + return Integer.toString(resultCode); + } + return text + "(" + resultCode + ")"; + } } diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java index 8331550a7ef5..cc1b6cda82bb 100644 --- a/core/java/android/service/autofill/FillContext.java +++ b/core/java/android/service/autofill/FillContext.java @@ -32,7 +32,7 @@ import android.view.autofill.AutofillId; import com.android.internal.util.DataClass; -import java.util.LinkedList; +import java.util.ArrayDeque; /** * This class represents a context for each fill request made via {@link @@ -95,7 +95,7 @@ public final class FillContext implements Parcelable { * @hide */ @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) { - final LinkedList<ViewNode> nodesToProcess = new LinkedList<>(); + final ArrayDeque<ViewNode> nodesToProcess = new ArrayDeque<>(); final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length]; // Indexes of foundNodes that are not found yet diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 1507c87c0452..327cda3360bb 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -97,7 +97,7 @@ public final class FillRequest implements Parcelable { */ public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10; - // The flag value 0x20 has been used. + // The flag value 0x20 has been defined in AutofillManager. /** * Indicates the request supports fill dialog presentation for the fields, the diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java index 0d290eee5777..ce38bb823fb3 100644 --- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java @@ -135,7 +135,7 @@ class QuickAccessWalletServiceInfo { return null; } - private static class ServiceMetadata { + static class ServiceMetadata { @Nullable private final String mSettingsActivity; @Nullable @@ -161,7 +161,7 @@ class QuickAccessWalletServiceInfo { } } - private static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) { + static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) { PackageManager pm = context.getPackageManager(); final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm, QuickAccessWalletService.SERVICE_META_DATA); diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java index 01d5638461af..5b3b78b65786 100644 --- a/core/java/android/service/voice/AbstractHotwordDetector.java +++ b/core/java/android/service/voice/AbstractHotwordDetector.java @@ -22,6 +22,7 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; +import android.app.compat.CompatChanges; import android.media.AudioFormat; import android.media.permission.Identity; import android.os.Handler; @@ -65,6 +66,13 @@ abstract class AbstractHotwordDetector implements HotwordDetector { } /** + * Method to be called for the detector to ready/register itself with underlying system + * services. + */ + abstract void initialize(@Nullable PersistableBundle options, + @Nullable SharedMemory sharedMemory); + + /** * Detect hotword from an externally supplied stream of data. * * @return true if the request to start recognition succeeded @@ -73,7 +81,7 @@ abstract class AbstractHotwordDetector implements HotwordDetector { public boolean startRecognition( @NonNull ParcelFileDescriptor audioStream, @NonNull AudioFormat audioFormat, - @Nullable PersistableBundle options) { + @Nullable PersistableBundle options) throws IllegalDetectorStateException { if (DEBUG) { Slog.i(TAG, "#recognizeHotword"); } @@ -98,19 +106,22 @@ abstract class AbstractHotwordDetector implements HotwordDetector { * Set configuration and pass read-only data to hotword detection service. * * @param options Application configuration data to provide to the - * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or - * other contents that can be used to communicate with other processes. + * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable + * objects or other contents that can be used to communicate with other processes. * @param sharedMemory The unrestricted data blob to provide to the - * {@link HotwordDetectionService}. Use this to provide the hotword models data or other - * such data to the trusted process. - * - * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a - * {@link HotwordDetectionService} when it was created. In addition, if this - * AlwaysOnHotwordDetector is in an invalid or error state. + * {@link HotwordDetectionService}. Use this to provide the hotword models data or other + * such data to the trusted process. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of + * Android Tiramisu or above and attempts to start a recognition when the detector is + * not able based on the state. Because the caller receives updates via an asynchronous + * callback and the state of the detector can change without caller's knowledge, a + * checked exception is thrown. + * @throws IllegalStateException if this HotwordDetector wasn't specified to use a + * {@link HotwordDetectionService} when it was created. */ @Override public void updateState(@Nullable PersistableBundle options, - @Nullable SharedMemory sharedMemory) { + @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException { if (DEBUG) { Slog.d(TAG, "updateState()"); } @@ -156,9 +167,13 @@ abstract class AbstractHotwordDetector implements HotwordDetector { } } - protected void throwIfDetectorIsNoLongerActive() { + protected void throwIfDetectorIsNoLongerActive() throws IllegalDetectorStateException { if (!mIsDetectorActive.get()) { Slog.e(TAG, "attempting to use a destroyed detector which is no longer active"); + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "attempting to use a destroyed detector which is no longer active"); + } throw new IllegalStateException( "attempting to use a destroyed detector which is no longer active"); } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index bc42da6b4c97..d01e7feba36e 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -18,6 +18,7 @@ package android.service.voice; import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; +import static android.service.voice.VoiceInteractionService.MULTIPLE_ACTIVE_HOTWORD_DETECTORS; import android.annotation.IntDef; import android.annotation.NonNull; @@ -27,6 +28,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.ActivityThread; +import android.app.compat.CompatChanges; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -50,9 +52,11 @@ import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SharedMemory; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionManagerService; import com.android.internal.app.IVoiceInteractionSoundTriggerSession; @@ -62,8 +66,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; +import java.util.Set; /** * A class that lets a VoiceInteractionService implementation interact with @@ -275,11 +282,12 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * The metadata of the Keyphrase, derived from the enrollment application. * This may be null if this keyphrase isn't supported by the enrollment application. */ + @GuardedBy("mLock") @Nullable private KeyphraseMetadata mKeyphraseMetadata; private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo; private final IVoiceInteractionManagerService mModelManagementService; - private final IVoiceInteractionSoundTriggerSession mSoundTriggerSession; + private IVoiceInteractionSoundTriggerSession mSoundTriggerSession; private final SoundTriggerListener mInternalCallback; private final Callback mExternalCallback; private final Handler mHandler; @@ -287,6 +295,9 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { private final int mTargetSdkVersion; private final boolean mSupportHotwordDetectionService; + @GuardedBy("mLock") + private boolean mIsAvailabilityOverriddenByTestApi = false; + @GuardedBy("mLock") private int mAvailability = STATE_NOT_READY; /** @@ -788,8 +799,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback, KeyphraseEnrollmentInfo keyphraseEnrollmentInfo, IVoiceInteractionManagerService modelManagementService, int targetSdkVersion, - boolean supportHotwordDetectionService, @Nullable PersistableBundle options, - @Nullable SharedMemory sharedMemory) { + boolean supportHotwordDetectionService) { super(modelManagementService, callback, supportHotwordDetectionService ? DETECTOR_TYPE_TRUSTED_HOTWORD_DSP : DETECTOR_TYPE_NORMAL); @@ -803,6 +813,12 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { mModelManagementService = modelManagementService; mTargetSdkVersion = targetSdkVersion; mSupportHotwordDetectionService = supportHotwordDetectionService; + } + + @Override + void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) { + // TODO: transition to use an API that is not updateState to provide + // onHotwordDetectionServiceInitialized status to external callback if (mSupportHotwordDetectionService) { updateStateLocked(options, sharedMemory, mInternalCallback, DETECTOR_TYPE_TRUSTED_HOTWORD_DSP); @@ -810,30 +826,40 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { try { Identity identity = new Identity(); identity.packageName = ActivityThread.currentOpPackageName(); - mSoundTriggerSession = mModelManagementService.createSoundTriggerSessionAsOriginator( - identity, mBinder); + mSoundTriggerSession = + mModelManagementService.createSoundTriggerSessionAsOriginator( + identity, mBinder); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } - new RefreshAvailabiltyTask().execute(); + new RefreshAvailabilityTask().execute(); } /** * {@inheritDoc} * - * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a - * {@link HotwordDetectionService} when it was created. In addition, if this - * AlwaysOnHotwordDetector is in an invalid or error state. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above and this AlwaysOnHotwordDetector wasn't specified to use a + * {@link HotwordDetectionService} when it was created. In addition, the exception can + * be thrown if this AlwaysOnHotwordDetector is in an invalid or error state. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if + * this AlwaysOnHotwordDetector wasn't specified to use a + * {@link HotwordDetectionService} when it was created. In addition, the exception can + * be thrown if this AlwaysOnHotwordDetector is in an invalid or error state. */ @Override public final void updateState(@Nullable PersistableBundle options, - @Nullable SharedMemory sharedMemory) { + @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException { synchronized (mLock) { if (!mSupportHotwordDetectionService) { throw new IllegalStateException( "updateState called, but it doesn't support hotword detection service"); } if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "updateState called on an invalid detector or error state"); + } throw new IllegalStateException( "updateState called on an invalid detector or error state"); } @@ -843,6 +869,48 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } /** + * Test API for manipulating the voice engine and sound model availability. + * + * After overriding the availability status, the client's + * {@link Callback#onAvailabilityChanged(int)} will be called to reflect the updated state. + * + * When this override is set, all system updates to availability will be ignored. + * @hide + */ + @TestApi + public void overrideAvailability(int availability) { + synchronized (mLock) { + // ENROLLED state requires there to be metadata about the sound model so a fake one + // is created. + if (mKeyphraseMetadata == null && availability == STATE_KEYPHRASE_ENROLLED) { + Set<Locale> fakeSupportedLocales = new HashSet<>(); + fakeSupportedLocales.add(mLocale); + mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales, + AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER); + } + + mAvailability = availability; + mIsAvailabilityOverriddenByTestApi = true; + notifyStateChangedLocked(); + } + } + + /** + * Test API for clearing an availability override set by {@link #overrideAvailability(int)} + * + * This method will restore the availability to the current system state. + * @hide + */ + @TestApi + public void resetAvailability() { + synchronized (mLock) { + mIsAvailabilityOverriddenByTestApi = false; + } + // Execute a refresh availability task - which should then notify of a change. + new RefreshAvailabilityTask().execute(); + } + + /** * Test API to simulate to trigger hardware recognition event for test. * * @hide @@ -878,28 +946,46 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * @see #RECOGNITION_MODE_USER_IDENTIFICATION * @see #RECOGNITION_MODE_VOICE_TRIGGER * - * @throws UnsupportedOperationException if the keyphrase itself isn't supported. - * Callers should only call this method after a supported state callback on - * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid or error state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above. Because the caller receives availability updates via an asynchronous + * callback, it may be due to the availability changing while this call is performed. + * - Throws if the detector is in an invalid or error state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level + * 33 Android if the recognition isn't supported. Callers should only call this method + * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to + * avoid this exception. + * @throws IllegalStateException Thrown when a caller has a target SDK below Android API level + * 33 if the detector is in an invalid or error state. This may happen if another + * detector has been instantiated or the {@link VoiceInteractionService} hosting this + * detector has been shut down. */ - public @RecognitionModes int getSupportedRecognitionModes() { + public @RecognitionModes + int getSupportedRecognitionModes() throws IllegalDetectorStateException { if (DBG) Slog.d(TAG, "getSupportedRecognitionModes()"); synchronized (mLock) { return getSupportedRecognitionModesLocked(); } } - private int getSupportedRecognitionModesLocked() { + @GuardedBy("mLock") + private int getSupportedRecognitionModesLocked() throws IllegalDetectorStateException { if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException("getSupportedRecognitionModes called on an" + + " invalid detector or error state"); + } throw new IllegalStateException( "getSupportedRecognitionModes called on an invalid detector or error state"); } // This method only makes sense if we can actually support a recognition. if (mAvailability != STATE_KEYPHRASE_ENROLLED || mKeyphraseMetadata == null) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException("Getting supported recognition modes for" + + " the keyphrase is not supported"); + } throw new UnsupportedOperationException( "Getting supported recognition modes for the keyphrase is not supported"); } @@ -926,6 +1012,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } } + @GuardedBy("mLock") private int getSupportedAudioCapabilitiesLocked() { try { ModuleProperties properties = @@ -949,30 +1036,77 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS * * @param recognitionFlags The flags to control the recognition properties. + * @param data Additional pass-through data to the system voice engine along with the + * startRecognition request. This data is intended to provide additional parameters + * when starting the opaque sound model. * @return Indicates whether the call succeeded or not. - * @throws UnsupportedOperationException if the recognition isn't supported. - * Callers should only call this method after a supported state callback on - * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid or error state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above and attempts to start a recognition when the detector is not able based on + * the availability state. This can be thrown even if the state has been checked before + * calling this method because the caller receives availability updates via an + * asynchronous callback, it may be due to the availability changing while this call is + * performed. + * - Throws if the recognition isn't supported. + * Callers should only call this method after a supported state callback on + * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * - Also throws if the detector is in an invalid or error state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level + * 33 Android if the recognition isn't supported. Callers should only call this method + * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to + * avoid this exception. + * @throws IllegalStateException Thrown when a caller has a target SDK below Android API level + * 33 if the detector is in an invalid or error state. This may happen if another + * detector has been instantiated or the {@link VoiceInteractionService} hosting this + * detector has been shut down. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) - public boolean startRecognition(@RecognitionFlags int recognitionFlags) { - if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")"); + public boolean startRecognition(@RecognitionFlags int recognitionFlags, @NonNull byte[] data) + throws IllegalDetectorStateException { synchronized (mLock) { - if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { - throw new IllegalStateException( - "startRecognition called on an invalid detector or error state"); - } - - // Check if we can start/stop a recognition. - if (mAvailability != STATE_KEYPHRASE_ENROLLED) { - throw new UnsupportedOperationException( - "Recognition for the given keyphrase is not supported"); - } + return startRecognitionLocked(recognitionFlags, data) + == STATUS_OK; + } + } - return startRecognitionLocked(recognitionFlags) == STATUS_OK; + /** + * Starts recognition for the associated keyphrase. + * Caller must be the active voice interaction service via + * Settings.Secure.VOICE_INTERACTION_SERVICE. + * + * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO + * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS + * + * @param recognitionFlags The flags to control the recognition properties. + * @return Indicates whether the call succeeded or not. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above and attempts to start a recognition when the detector is not able based on + * the availability state. This can be thrown even if the state has been checked before + * calling this method because the caller receives availability updates via an + * asynchronous callback, it may be due to the availability changing while this call is + * performed. + * - Throws if the recognition isn't supported. + * Callers should only call this method after a supported state callback on + * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * - Also throws if the detector is in an invalid or error state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level + * 33 if the recognition isn't supported. Callers should only call this method after a + * supported state callback on {@link Callback#onAvailabilityChanged(int)} to avoid this + * exception. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the + * detector is in an invalid or error state. This may happen if another detector has + * been instantiated or the {@link VoiceInteractionService} hosting this detector has + * been shut down. + */ + @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) + public boolean startRecognition(@RecognitionFlags int recognitionFlags) + throws IllegalDetectorStateException { + if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")"); + synchronized (mLock) { + return startRecognitionLocked(recognitionFlags, null /* data */) == STATUS_OK; } } @@ -983,7 +1117,8 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) @Override - public boolean startRecognition() { + public boolean startRecognition() + throws IllegalDetectorStateException { return startRecognition(0); } @@ -993,28 +1128,44 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * Settings.Secure.VOICE_INTERACTION_SERVICE. * * @return Indicates whether the call succeeded or not. - * @throws UnsupportedOperationException if the recognition isn't supported. - * Callers should only call this method after a supported state callback on - * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid or error state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of + * API level 33 or above and attempts to stop a recognition when the detector is + * not able based on the state. This can be thrown even if the state has been checked + * before calling this method because the caller receives availability updates via an + * asynchronous callback, it may be due to the availability changing while this call is + * performed. + * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level + * 33 if the recognition isn't supported. Callers should only call this method after a + * supported state callback on {@link Callback#onAvailabilityChanged(int)} to avoid this + * exception. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the + * detector is in an invalid or error state. This may happen if another detector has + * been instantiated or the {@link VoiceInteractionService} hosting this detector has + * been shut down. */ // TODO: Remove this RequiresPermission since it isn't actually enforced. Also fix the javadoc // about permissions enforcement (when it throws vs when it just returns false) for other // methods in this class. @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) @Override - public boolean stopRecognition() { + public boolean stopRecognition() throws IllegalDetectorStateException { if (DBG) Slog.d(TAG, "stopRecognition()"); synchronized (mLock) { if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "stopRecognition called on an invalid detector or error state"); + } throw new IllegalStateException( "stopRecognition called on an invalid detector or error state"); } // Check if we can start/stop a recognition. if (mAvailability != STATE_KEYPHRASE_ENROLLED) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "Recognition for the given keyphrase is not supported"); + } throw new UnsupportedOperationException( "Recognition for the given keyphrase is not supported"); } @@ -1039,18 +1190,28 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or * if API is not supported by HAL - * @throws IllegalStateException if the detector is in an invalid or error state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * if the detector is in an invalid or error state. This may happen if another detector + * has been instantiated or the {@link VoiceInteractionService} hosting this detector + * has been shut down. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the + * detector is in an invalid or error state. This may happen if another detector has + * been instantiated or the {@link VoiceInteractionService} hosting this detector has + * been shut down. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) - public int setParameter(@ModelParams int modelParam, int value) { + public int setParameter(@ModelParams int modelParam, int value) + throws IllegalDetectorStateException { if (DBG) { Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")"); } synchronized (mLock) { if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "setParameter called on an invalid detector or error state"); + } throw new IllegalStateException( "setParameter called on an invalid detector or error state"); } @@ -1071,18 +1232,27 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * * @param modelParam {@link ModelParams} * @return value of parameter - * @throws IllegalStateException if the detector is in an invalid or error state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * if the detector is in an invalid or error state. This may happen if another detector + * has been instantiated or the {@link VoiceInteractionService} hosting this detector + * has been shut down. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if + * the detector is in an invalid or error state. This may happen if another detector has + * been instantiated or the {@link VoiceInteractionService} hosting this detector has + * been shut down. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) - public int getParameter(@ModelParams int modelParam) { + public int getParameter(@ModelParams int modelParam) throws IllegalDetectorStateException { if (DBG) { Slog.d(TAG, "getParameter(" + modelParam + ")"); } synchronized (mLock) { if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "getParameter called on an invalid detector or error state"); + } throw new IllegalStateException( "getParameter called on an invalid detector or error state"); } @@ -1100,19 +1270,29 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * * @param modelParam {@link ModelParams} * @return supported range of parameter, null if not supported - * @throws IllegalStateException if the detector is in an invalid or error state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * if the detector is in an invalid or error state. This may happen if another detector + * has been instantiated or the {@link VoiceInteractionService} hosting this detector + * has been shut down. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if + * the detector is in an invalid or error state. This may happen if another detector has + * been instantiated or the {@link VoiceInteractionService} hosting this detector has + * been shut down. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) @Nullable - public ModelParamRange queryParameter(@ModelParams int modelParam) { + public ModelParamRange queryParameter(@ModelParams int modelParam) + throws IllegalDetectorStateException { if (DBG) { Slog.d(TAG, "queryParameter(" + modelParam + ")"); } synchronized (mLock) { if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "queryParameter called on an invalid detector or error state"); + } throw new IllegalStateException( "queryParameter called on an invalid detector or error state"); } @@ -1129,15 +1309,25 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * otherwise {@link #createReEnrollIntent()} should be preferred. * * @return An {@link Intent} to start enrollment for the given keyphrase. - * @throws UnsupportedOperationException if managing they keyphrase isn't supported. - * Callers should only call this method after a supported state callback on - * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above. + * - Thrown if managing they keyphrase isn't supported. Callers should only call this + * method after a supported state callback on + * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * - Thrown if the detector is in an invalid state. This may happen if another detector + * has been instantiated or the {@link VoiceInteractionService} hosting this detector + * has been shut down. + * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level + * 33 if managing they keyphrase isn't supported. Callers should only call this method + * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to + * avoid this exception. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the + * detector is in an invalid state. This may happen if another detector has been + * instantiated or the {@link VoiceInteractionService} hosting this detector has been + * shut down. */ @Nullable - public Intent createEnrollIntent() { + public Intent createEnrollIntent() throws IllegalDetectorStateException { if (DBG) Slog.d(TAG, "createEnrollIntent"); synchronized (mLock) { return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_ENROLL); @@ -1151,15 +1341,25 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error. * * @return An {@link Intent} to start un-enrollment for the given keyphrase. - * @throws UnsupportedOperationException if managing they keyphrase isn't supported. - * Callers should only call this method after a supported state callback on - * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above. + * - Thrown if managing they keyphrase isn't supported. Callers should only call this + * method after a supported state callback on + * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * - Thrown if the detector is in an invalid state. This may happen if another detector + * has been instantiated or the {@link VoiceInteractionService} hosting this detector + * has been shut down. + * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level + * 33 if managing they keyphrase isn't supported. Callers should only call this method + * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to + * avoid this exception. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the + * detector is in an invalid state. This may happen if another detector has been + * instantiated or the {@link VoiceInteractionService} hosting this detector has been + * shut down. */ @Nullable - public Intent createUnEnrollIntent() { + public Intent createUnEnrollIntent() throws IllegalDetectorStateException { if (DBG) Slog.d(TAG, "createUnEnrollIntent"); synchronized (mLock) { return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_UN_ENROLL); @@ -1173,30 +1373,50 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error. * * @return An {@link Intent} to start re-enrollment for the given keyphrase. - * @throws UnsupportedOperationException if managing they keyphrase isn't supported. - * Callers should only call this method after a supported state callback on - * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid or error state. - * This may happen if another detector has been instantiated or the - * {@link VoiceInteractionService} hosting this detector has been shut down. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above. + * - Thrown if managing they keyphrase isn't supported. Callers should only call this + * method after a supported state callback on + * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * - Thrown if the detector is in an invalid state. This may happen if another detector + * has been instantiated or the {@link VoiceInteractionService} hosting this detector + * has been shut down. + * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level + * 33 if managing they keyphrase isn't supported. Callers should only call this method + * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to + * avoid this exception. + * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the + * detector is in an invalid state. This may happen if another detector has been + * instantiated or the {@link VoiceInteractionService} hosting this detector has been + * shut down. */ @Nullable - public Intent createReEnrollIntent() { + public Intent createReEnrollIntent() throws IllegalDetectorStateException { if (DBG) Slog.d(TAG, "createReEnrollIntent"); synchronized (mLock) { return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_RE_ENROLL); } } - private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action) { + @GuardedBy("mLock") + private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action) + throws IllegalDetectorStateException { if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "getManageIntent called on an invalid detector or error state"); + } throw new IllegalStateException( - "getManageIntent called on an invalid detector or error state"); + "getManageIntent called on an invalid detector or error state"); } // This method only makes sense if we can actually support a recognition. if (mAvailability != STATE_KEYPHRASE_ENROLLED && mAvailability != STATE_KEYPHRASE_UNENROLLED) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "Managing the given keyphrase is not supported"); + } throw new UnsupportedOperationException( "Managing the given keyphrase is not supported"); } @@ -1212,24 +1432,29 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { public void destroy() { synchronized (mLock) { if (mAvailability == STATE_KEYPHRASE_ENROLLED) { - stopRecognition(); + try { + stopRecognition(); + } catch (Exception e) { + Log.i(TAG, "failed to stopRecognition in destroy", e); + } } mAvailability = STATE_INVALID; + mIsAvailabilityOverriddenByTestApi = false; notifyStateChangedLocked(); - - if (mSupportHotwordDetectionService) { - try { - mModelManagementService.shutdownHotwordDetectionService(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } } super.destroy(); } /** + * @hide + */ + @Override + public boolean isUsingHotwordDetectionService() { + return mSupportHotwordDetectionService; + } + + /** * Reloads the sound models from the service. * * @hide @@ -1244,6 +1469,15 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { return; } + // Because this method reflects an update from the system service models, we should not + // update the client of an availability change when the availability has been overridden + // via a test API. + if (mIsAvailabilityOverriddenByTestApi) { + Slog.w(TAG, "Suppressing system availability update. " + + "Availability is overridden by test API."); + return; + } + // Stop the recognition before proceeding. // This is done because we want to stop the recognition on an older model if it changed // or was deleted. @@ -1263,11 +1497,37 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } // Execute a refresh availability task - which should then notify of a change. - new RefreshAvailabiltyTask().execute(); + new RefreshAvailabilityTask().execute(); } } - private int startRecognitionLocked(int recognitionFlags) { + @GuardedBy("mLock") + private int startRecognitionLocked(int recognitionFlags, + @Nullable byte[] data) throws IllegalDetectorStateException { + if (DBG) { + Slog.d(TAG, "startRecognition(" + + recognitionFlags + + ", " + Arrays.toString(data) + ")"); + } + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "startRecognition called on an invalid detector or error state"); + } + throw new IllegalStateException( + "startRecognition called on an invalid detector or error state"); + } + + // Check if we can start/stop a recognition. + if (mAvailability != STATE_KEYPHRASE_ENROLLED) { + if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) { + throw new IllegalDetectorStateException( + "Recognition for the given keyphrase is not supported"); + } + throw new UnsupportedOperationException( + "Recognition for the given keyphrase is not supported"); + } + KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1]; // TODO: Do we need to do something about the confidence level here? recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.getId(), @@ -1291,7 +1551,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { code = mSoundTriggerSession.startRecognition( mKeyphraseMetadata.getId(), mLocale.toLanguageTag(), mInternalCallback, new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers, - recognitionExtra, null /* additional data */, audioCapabilities), + recognitionExtra, data, audioCapabilities), runInBatterySaver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1303,6 +1563,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { return code; } + @GuardedBy("mLock") private int stopRecognitionLocked() { int code; try { @@ -1318,6 +1579,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { return code; } + @GuardedBy("mLock") private int setParameterLocked(@ModelParams int modelParam, int value) { try { int code = mSoundTriggerSession.setParameter(mKeyphraseMetadata.getId(), modelParam, @@ -1333,6 +1595,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } } + @GuardedBy("mLock") private int getParameterLocked(@ModelParams int modelParam) { try { return mSoundTriggerSession.getParameter(mKeyphraseMetadata.getId(), modelParam); @@ -1341,6 +1604,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } } + @GuardedBy("mLock") @Nullable private ModelParamRange queryParameterLocked(@ModelParams int modelParam) { try { @@ -1357,15 +1621,19 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } } + @GuardedBy("mLock") private void updateAndNotifyStateChangedLocked(int availability) { if (DBG) { Slog.d(TAG, "Hotword availability changed from " + mAvailability + " -> " + availability); } - mAvailability = availability; + if (!mIsAvailabilityOverriddenByTestApi) { + mAvailability = availability; + } notifyStateChangedLocked(); } + @GuardedBy("mLock") private void notifyStateChangedLocked() { Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED); message.arg1 = mAvailability; @@ -1487,7 +1755,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } } - class RefreshAvailabiltyTask extends AsyncTask<Void, Void, Void> { + class RefreshAvailabilityTask extends AsyncTask<Void, Void, Void> { @Override public Void doInBackground(Void... params) { @@ -1555,7 +1823,26 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } } + @Override + public boolean equals(Object obj) { + if (CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) { + if (!(obj instanceof AlwaysOnHotwordDetector)) { + return false; + } + AlwaysOnHotwordDetector other = (AlwaysOnHotwordDetector) obj; + return TextUtils.equals(mText, other.mText) && mLocale.equals(other.mLocale); + } + + return super.equals(obj); + } + + @Override + public int hashCode() { + return Objects.hash(mText, mLocale); + } + /** @hide */ + @Override public void dump(String prefix, PrintWriter pw) { synchronized (mLock) { pw.print(prefix); pw.print("Text="); pw.println(mText); diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index dfe0f542b3ca..19e248fc5cf5 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -243,13 +243,26 @@ public abstract class HotwordDetectionService extends Service { /** * Called when the device hardware (such as a DSP) detected the hotword, to request second stage * validation before handing over the audio to the {@link AlwaysOnHotwordDetector}. - * <p> - * After {@code callback} is invoked or {@code timeoutMillis} has passed, and invokes the + * + * <p>After {@code callback} is invoked or {@code timeoutMillis} has passed, and invokes the * appropriate {@link AlwaysOnHotwordDetector.Callback callback}. * + * <p>When responding to a detection event, the + * {@link HotwordDetectedResult#getHotwordPhraseId()} must match a keyphrase ID listed + * in the eventPayload's + * {@link AlwaysOnHotwordDetector.EventPayload#getKeyphraseRecognitionExtras()} list. This is + * forcing the intention of the {@link HotwordDetectionService} to validate an event from the + * voice engine and not augment its result. + * * @param eventPayload Payload data for the hardware detection event. This may contain the - * trigger audio, if requested when calling - * {@link AlwaysOnHotwordDetector#startRecognition(int)}. + * trigger audio, if requested when calling + * {@link AlwaysOnHotwordDetector#startRecognition(int)}. + * Each {@link AlwaysOnHotwordDetector} will be associated with at minimum a unique + * keyphrase ID indicated by + * {@link AlwaysOnHotwordDetector.EventPayload#getKeyphraseRecognitionExtras()}[0]. + * Any extra + * {@link android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra}'s + * in the eventPayload represent additional phrases detected by the voice engine. * @param timeoutMillis Timeout in milliseconds for the operation to invoke the callback. If * the application fails to abide by the timeout, system will close the * microphone and cancel the operation. diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java index 96fd8bbda016..1a0dc8945b63 100644 --- a/core/java/android/service/voice/HotwordDetector.java +++ b/core/java/android/service/voice/HotwordDetector.java @@ -23,10 +23,16 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.media.AudioFormat; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.SharedMemory; +import android.util.AndroidException; + +import java.io.PrintWriter; /** * Basic functionality for hotword detectors. @@ -37,6 +43,23 @@ import android.os.SharedMemory; public interface HotwordDetector { /** + * Prior to API level 33, API calls of {@link android.service.voice.HotwordDetector} could + * return both {@link java.lang.IllegalStateException} or + * {@link java.lang.UnsupportedOperationException} depending on the detector's underlying state. + * This lead to confusing behavior as the underlying state of the detector can be modified + * without the knowledge of the caller via system service layer updates. + * + * This change ID, when enabled, changes the API calls to only throw checked exception + * {@link android.service.voice.HotwordDetector.IllegalDetectorStateException} when checking + * against state information modified by both the caller and the system services. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + long HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION = 226355112L; + + /** * Indicates that it is a non-trusted hotword detector. * * @hide @@ -74,16 +97,26 @@ public interface HotwordDetector { * Calling this again while recognition is active does nothing. * * @return true if the request to start recognition succeeded + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above and attempts to start a recognition when the detector is not able based on + * the state. This can be thrown even if the state has been checked before calling this + * method because the caller receives updates via an asynchronous callback, and the + * state of the detector can change concurrently to the caller calling this method. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) - boolean startRecognition(); + boolean startRecognition() throws IllegalDetectorStateException; /** * Stops hotword recognition. * * @return true if the request to stop recognition succeeded + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above and attempts to stop a recognition when the detector is not able based on + * the state. This can be thrown even if the state has been checked before calling this + * method because the caller receives updates via an asynchronous callback, and the + * state of the detector can change concurrently to the caller calling this method. */ - boolean stopRecognition(); + boolean stopRecognition() throws IllegalDetectorStateException; /** * Starts hotword recognition on audio coming from an external connected microphone. @@ -97,26 +130,37 @@ public interface HotwordDetector { * PersistableBundle does not allow any remotable objects or other contents that can be * used to communicate with other processes. * @return true if the request to start recognition succeeded + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above and attempts to start a recognition when the detector is not able based on + * the state. This can be thrown even if the state has been checked before calling this + * method because the caller receives updates via an asynchronous callback, and the + * state of the detector can change concurrently to the caller calling this method. */ boolean startRecognition( @NonNull ParcelFileDescriptor audioStream, @NonNull AudioFormat audioFormat, - @Nullable PersistableBundle options); + @Nullable PersistableBundle options) throws IllegalDetectorStateException; /** * Set configuration and pass read-only data to hotword detection service. * * @param options Application configuration data to provide to the - * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or - * other contents that can be used to communicate with other processes. + * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable + * objects or other contents that can be used to communicate with other processes. * @param sharedMemory The unrestricted data blob to provide to the - * {@link HotwordDetectionService}. Use this to provide the hotword models data or other - * such data to the trusted process. - * + * {@link HotwordDetectionService}. Use this to provide the hotword models data or other + * such data to the trusted process. + * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 + * or above and the detector is not able to perform the operation based on the + * underlying state. This can be thrown even if the state has been checked before + * calling this method because the caller receives updates via an asynchronous callback, + * and the state of the detector can change concurrently to the caller calling this + * method. * @throws IllegalStateException if this HotwordDetector wasn't specified to use a - * {@link HotwordDetectionService} when it was created. + * {@link HotwordDetectionService} when it was created. */ - void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory); + void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) + throws IllegalDetectorStateException; /** * Invalidates this hotword detector so that any future calls to this result @@ -132,6 +176,13 @@ public interface HotwordDetector { /** * @hide */ + default boolean isUsingHotwordDetectionService() { + throw new UnsupportedOperationException("Not implemented. Must override in a subclass."); + } + + /** + * @hide + */ static String detectorTypeToString(int detectorType) { switch (detectorType) { case DETECTOR_TYPE_NORMAL: @@ -145,6 +196,11 @@ public interface HotwordDetector { } } + /** @hide */ + default void dump(String prefix, PrintWriter pw) { + throw new UnsupportedOperationException("Not implemented. Must override in a subclass."); + } + /** * The callback to notify of detection events. */ @@ -205,4 +261,14 @@ public interface HotwordDetector { */ void onHotwordDetectionServiceRestarted(); } + + /** + * {@link HotwordDetector} specific exception thrown when the underlying state of the detector + * is invalid for the given action. + */ + class IllegalDetectorStateException extends AndroidException { + IllegalDetectorStateException(String message) { + super(message); + } + } } diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index 2d662eaf0a4f..36ea91e4edb5 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -30,6 +30,7 @@ import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SharedMemory; +import android.util.Log; import android.util.Slog; import com.android.internal.app.IHotwordRecognitionStatusCallback; @@ -57,8 +58,6 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { SoftwareHotwordDetector( IVoiceInteractionManagerService managerService, AudioFormat audioFormat, - PersistableBundle options, - SharedMemory sharedMemory, HotwordDetector.Callback callback) { super(managerService, callback, DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE); @@ -66,6 +65,12 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { mAudioFormat = audioFormat; mCallback = callback; mHandler = new Handler(Looper.getMainLooper()); + } + + @Override + void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) { + // TODO: transition to use an API that is not updateState to provide + // onHotwordDetectionServiceInitialized status to external callback updateStateLocked(options, sharedMemory, new InitializationStateListener(mHandler, mCallback), DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE); @@ -73,7 +78,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { @RequiresPermission(RECORD_AUDIO) @Override - public boolean startRecognition() { + public boolean startRecognition() throws IllegalDetectorStateException { if (DEBUG) { Slog.i(TAG, "#startRecognition"); } @@ -96,7 +101,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { /** TODO: stopRecognition */ @RequiresPermission(RECORD_AUDIO) @Override - public boolean stopRecognition() { + public boolean stopRecognition() throws IllegalDetectorStateException { if (DEBUG) { Slog.i(TAG, "#stopRecognition"); } @@ -113,17 +118,23 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { @Override public void destroy() { - stopRecognition(); - maybeCloseExistingSession(); - try { - mManagerService.shutdownHotwordDetectionService(); - } catch (RemoteException ex) { - ex.rethrowFromSystemServer(); + stopRecognition(); + } catch (Exception e) { + Log.i(TAG, "failed to stopRecognition in destroy", e); } + maybeCloseExistingSession(); super.destroy(); } + /** + * @hide + */ + @Override + public boolean isUsingHotwordDetectionService() { + return true; + } + private void maybeCloseExistingSession() { // TODO: needs to be synchronized. // TODO: implement this @@ -231,6 +242,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { } /** @hide */ + @Override public void dump(String prefix, PrintWriter pw) { // TODO: implement this } diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index bf0cfbe49f31..1170237a346e 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -24,12 +24,16 @@ import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.Service; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.media.voice.KeyphraseModelManager; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -89,6 +93,37 @@ public class VoiceInteractionService extends Service { */ public static final String SERVICE_META_DATA = "android.voice_interaction"; + /** + * For apps targeting Build.VERSION_CODES.TRAMISU and above, implementors of this + * service can create multiple AlwaysOnHotwordDetector instances in parallel. They will + * also e ale to create a single SoftwareHotwordDetector in parallel with any other + * active AlwaysOnHotwordDetector instances. + * + * <p>Requirements when this change is enabled: + * <ul> + * <li> + * Any number of AlwaysOnHotwordDetector instances can be created in parallel + * as long as they are unique to any other active AlwaysOnHotwordDetector. + * </li> + * <li> + * Only a single instance of SoftwareHotwordDetector can be active at a given + * time. It can be active at the same time as any number of + * AlwaysOnHotwordDetector instances. + * </li> + * <li> + * To release that reference and any resources associated with that reference, + * HotwordDetector#destroy() must be called. An attempt to create an + * HotwordDetector equal to an active HotwordDetector will be rejected + * until HotwordDetector#destroy() is called on the active instance. + * </li> + * </ul> + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT) + static final long MULTIPLE_ACTIVE_HOTWORD_DETECTORS = 193232191L; + IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() { @Override public void ready() { @@ -133,8 +168,7 @@ public class VoiceInteractionService extends Service { private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo; - private AlwaysOnHotwordDetector mHotwordDetector; - private SoftwareHotwordDetector mSoftwareHotwordDetector; + private final Set<HotwordDetector> mActiveHotwordDetectors = new ArraySet<>(); /** * Called when a user has activated an affordance to launch voice assist from the Keyguard. @@ -284,10 +318,12 @@ public class VoiceInteractionService extends Service { private void onSoundModelsChangedInternal() { synchronized (this) { - if (mHotwordDetector != null) { - // TODO: Stop recognition if a sound model that was being recognized gets deleted. - mHotwordDetector.onSoundModelsChanged(); - } + // TODO: Stop recognition if a sound model that was being recognized gets deleted. + mActiveHotwordDetectors.forEach(detector -> { + if (detector instanceof AlwaysOnHotwordDetector) { + ((AlwaysOnHotwordDetector) detector).onSoundModelsChanged(); + } + }); } } @@ -379,16 +415,31 @@ public class VoiceInteractionService extends Service { throw new IllegalStateException("Not available until onReady() is called"); } synchronized (mLock) { - // Allow only one concurrent recognition via the APIs. - safelyShutdownAllHotwordDetectors(); - mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback, - mKeyphraseEnrollmentInfo, mSystemService, + if (!CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) { + // Allow only one concurrent recognition via the APIs. + safelyShutdownAllHotwordDetectors(); + } + + AlwaysOnHotwordDetector dspDetector = new AlwaysOnHotwordDetector(keyphrase, locale, + callback, mKeyphraseEnrollmentInfo, mSystemService, getApplicationContext().getApplicationInfo().targetSdkVersion, - supportHotwordDetectionService, options, sharedMemory); - mHotwordDetector.registerOnDestroyListener((detector) -> onDspHotwordDetectorDestroyed( - (AlwaysOnHotwordDetector) detector)); + supportHotwordDetectionService); + if (!mActiveHotwordDetectors.add(dspDetector)) { + throw new IllegalArgumentException( + "the keyphrase=" + keyphrase + " and locale=" + locale + + " are already used by another always-on detector"); + } + + try { + dspDetector.registerOnDestroyListener(this::onHotwordDetectorDestroyed); + dspDetector.initialize(options, sharedMemory); + } catch (Exception e) { + mActiveHotwordDetectors.remove(dspDetector); + dspDetector.destroy(); + throw e; + } + return dspDetector; } - return mHotwordDetector; } /** @@ -434,16 +485,34 @@ public class VoiceInteractionService extends Service { throw new IllegalStateException("Not available until onReady() is called"); } synchronized (mLock) { - // Allow only one concurrent recognition via the APIs. - safelyShutdownAllHotwordDetectors(); - mSoftwareHotwordDetector = + if (!CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) { + // Allow only one concurrent recognition via the APIs. + safelyShutdownAllHotwordDetectors(); + } else { + for (HotwordDetector detector : mActiveHotwordDetectors) { + if (detector instanceof SoftwareHotwordDetector) { + throw new IllegalArgumentException( + "There is already an active SoftwareHotwordDetector. " + + "It must be destroyed to create a new one."); + } + } + } + + SoftwareHotwordDetector softwareHotwordDetector = new SoftwareHotwordDetector( - mSystemService, null, options, sharedMemory, callback); - mSoftwareHotwordDetector.registerOnDestroyListener( - (detector) -> onMicrophoneHotwordDetectorDestroyed( - (SoftwareHotwordDetector) detector)); + mSystemService, null, callback); + + try { + softwareHotwordDetector.registerOnDestroyListener( + this::onHotwordDetectorDestroyed); + softwareHotwordDetector.initialize(options, sharedMemory); + } catch (Exception e) { + mActiveHotwordDetectors.remove(softwareHotwordDetector); + softwareHotwordDetector.destroy(); + throw e; + } + return softwareHotwordDetector; } - return mSoftwareHotwordDetector; } /** @@ -489,33 +558,34 @@ public class VoiceInteractionService extends Service { private void safelyShutdownAllHotwordDetectors() { synchronized (mLock) { - if (mHotwordDetector != null) { + mActiveHotwordDetectors.forEach(detector -> { try { - mHotwordDetector.destroy(); + detector.destroy(); } catch (Exception ex) { - Log.i(TAG, "exception destroying AlwaysOnHotwordDetector", ex); + Log.i(TAG, "exception destroying HotwordDetector", ex); } - } - - if (mSoftwareHotwordDetector != null) { - try { - mSoftwareHotwordDetector.destroy(); - } catch (Exception ex) { - Log.i(TAG, "exception destroying SoftwareHotwordDetector", ex); - } - } + }); } } - private void onDspHotwordDetectorDestroyed(@NonNull AlwaysOnHotwordDetector detector) { + private void onHotwordDetectorDestroyed(@NonNull HotwordDetector detector) { synchronized (mLock) { - mHotwordDetector = null; + mActiveHotwordDetectors.remove(detector); + shutdownHotwordDetectionServiceIfRequiredLocked(); } } - private void onMicrophoneHotwordDetectorDestroyed(@NonNull SoftwareHotwordDetector detector) { - synchronized (mLock) { - mSoftwareHotwordDetector = null; + private void shutdownHotwordDetectionServiceIfRequiredLocked() { + for (HotwordDetector detector : mActiveHotwordDetectors) { + if (detector.isUsingHotwordDetectionService()) { + return; + } + } + + try { + mSystemService.shutdownHotwordDetectionService(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); } } @@ -540,18 +610,14 @@ public class VoiceInteractionService extends Service { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("VOICE INTERACTION"); synchronized (mLock) { - pw.println(" AlwaysOnHotwordDetector"); - if (mHotwordDetector == null) { - pw.println(" NULL"); - } else { - mHotwordDetector.dump(" ", pw); - } - - pw.println(" MicrophoneHotwordDetector"); - if (mSoftwareHotwordDetector == null) { + pw.println(" HotwordDetector(s)"); + if (mActiveHotwordDetectors.size() == 0) { pw.println(" NULL"); } else { - mSoftwareHotwordDetector.dump(" ", pw); + mActiveHotwordDetectors.forEach(detector -> { + detector.dump(" ", pw); + pw.println(); + }); } } } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 2f85d2b63840..520ceb2582db 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -25,6 +25,7 @@ import android.graphics.Paint; import android.graphics.text.LineBreakConfig; import android.graphics.text.LineBreaker; import android.os.Build; +import android.os.SystemProperties; import android.text.style.LeadingMarginSpan; import android.text.style.LeadingMarginSpan.LeadingMarginSpan2; import android.text.style.LineHeightSpan; @@ -32,6 +33,7 @@ import android.text.style.TabStopSpan; import android.util.Log; import android.util.Pools.SynchronizedPool; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; @@ -73,6 +75,13 @@ public class StaticLayout extends Layout { * default values. */ public final static class Builder { + // The content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE. + private static final int DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE = 3; + + // The property of content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE. + private static final String PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE = + "android.phrase.linecount.threshold"; + private Builder() {} /** @@ -431,11 +440,55 @@ public class StaticLayout extends Layout { */ @NonNull public StaticLayout build() { + reviseLineBreakConfig(); StaticLayout result = new StaticLayout(this); Builder.recycle(this); return result; } + private void reviseLineBreakConfig() { + boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking(); + int wordStyle = mLineBreakConfig.getLineBreakWordStyle(); + if (autoPhraseBreaking) { + if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) { + if (shouldEnablePhraseBreaking()) { + mLineBreakConfig = LineBreakConfig.getLineBreakConfig( + mLineBreakConfig.getLineBreakStyle(), + LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE, + mLineBreakConfig.getAutoPhraseBreaking()); + } + } + } + } + + private boolean shouldEnablePhraseBreaking() { + if (TextUtils.isEmpty(mText) || mWidth <= 0) { + return false; + } + int lineLimit = SystemProperties.getInt( + PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE, + DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE); + double desiredWidth = (double) Layout.getDesiredWidth(mText, mStart, + mEnd, mPaint, mTextDir); + int lineCount = (int) Math.ceil(desiredWidth / mWidth); + if (lineCount > 0 && lineCount <= lineLimit) { + return true; + } + return false; + } + + /** + * Get the line break word style. + * + * @return The current line break word style. + * + * @hide + */ + @VisibleForTesting + public int getLineBreakWordStyle() { + return mLineBreakConfig.getLineBreakWordStyle(); + } + private CharSequence mText; private int mStart; private int mEnd; diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 7ac6ae186cc3..51e36657e769 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -70,7 +70,6 @@ import android.util.Log; import android.util.Printer; import android.view.View; -import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; @@ -1230,8 +1229,8 @@ public class TextUtils { /** * Transforms a CharSequences to uppercase, copying the sources spans and keeping them spans as - * much as possible close to their relative original places. In the case the the uppercase - * string is identical to the sources, the source itself is returned instead of being copied. + * much as possible close to their relative original places. If uppercase string is identical + * to the sources, the source itself is returned instead of being copied. * * If copySpans is set, source must be an instance of Spanned. * diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 3be4c3edc10e..a173d80288d5 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -74,6 +74,14 @@ public class FeatureFlagUtils { public static final String SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE = "settings_hide_second_layer_page_navigate_up_button_in_two_pane"; + /** @hide */ + public static final String SETTINGS_AUTO_TEXT_WRAPPING = "settings_auto_text_wrapping"; + + /** Flag to enable/disable guest mode UX changes as mentioned in b/214031645 + * @hide + */ + public static final String SETTINGS_GUEST_MODE_UX_CHANGES = "settings_guest_mode_ux_changes"; + private static final Map<String, String> DEFAULT_FLAGS; static { @@ -100,6 +108,8 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true"); DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true"); DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true"); + DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false"); + DEFAULT_FLAGS.put(SETTINGS_GUEST_MODE_UX_CHANGES, "true"); } private static final Set<String> PERSISTENT_FLAGS; @@ -110,6 +120,7 @@ public class FeatureFlagUtils { PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS); PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME); PERSISTENT_FLAGS.add(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE); + PERSISTENT_FLAGS.add(SETTINGS_AUTO_TEXT_WRAPPING); } /** diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 0c4d9bf08583..3a684041e95c 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -26,11 +26,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.TestApi; -import android.app.ActivityThread; import android.app.KeyguardManager; import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -52,14 +50,11 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; -import com.android.internal.R; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Optional; /** * Provides information about the size and density of a logical display. @@ -116,12 +111,6 @@ public final class Display { private int mCachedAppHeightCompat; /** - * Cache if the application is the recents component. - * TODO(b/179308296) Remove once Launcher addresses issue - */ - private Optional<Boolean> mIsRecentsComponent = Optional.empty(); - - /** * The default Display id, which is the id of the primary display assuming there is one. */ public static final int DEFAULT_DISPLAY = 0; @@ -576,7 +565,8 @@ public final class Display { * @see com.android.service.display.DisplayDevice#hasStableUniqueId(). * @hide */ - public String getUniqueId() { + @TestApi + public @Nullable String getUniqueId() { return mDisplayInfo.uniqueId; } @@ -1584,36 +1574,7 @@ public final class Display { return false; } final Configuration config = mResources.getConfiguration(); - // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking - // if the caller is the recents component. - return config != null && !config.windowConfiguration.getMaxBounds().isEmpty() - && !isRecentsComponent(); - } - - /** - * Returns {@code true} when the calling package is the recents component. - * TODO(b/179308296) Remove once Launcher addresses issue - */ - boolean isRecentsComponent() { - if (mIsRecentsComponent.isPresent()) { - return mIsRecentsComponent.get(); - } - if (mResources == null) { - return false; - } - try { - String recentsComponent = mResources.getString(R.string.config_recentsComponentName); - if (recentsComponent == null) { - return false; - } - String recentsPackage = ComponentName.unflattenFromString(recentsComponent) - .getPackageName(); - mIsRecentsComponent = Optional.of(recentsPackage != null - && recentsPackage.equals(ActivityThread.currentPackageName())); - return mIsRecentsComponent.get(); - } catch (Resources.NotFoundException e) { - return false; - } + return config != null && !config.windowConfiguration.getMaxBounds().isEmpty(); } /** @@ -1631,6 +1592,21 @@ public final class Display { } /** + * Returns the committed state of the display. + * + * @return The latest committed display state, such as {@link #STATE_ON}. The display state + * {@link Display#getState()} is set as committed only after power state changes finish. + * + * @hide + */ + public int getCommittedState() { + synchronized (mLock) { + updateDisplayInfoLocked(); + return mIsValid ? mDisplayInfo.committedState : STATE_UNKNOWN; + } + } + + /** * Returns true if the specified UID has access to this display. * @hide */ diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 9264d2ed42a3..2aed319b1db9 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -253,6 +253,12 @@ public final class DisplayInfo implements Parcelable { public int state; /** + * The current committed state of the display. For example, this becomes + * {@link android.view.Display#STATE_ON} only after the power state ON is fully committed. + */ + public int committedState; + + /** * The UID of the application that owns this display, or zero if it is owned by the system. * <p> * If the display is private, then only the owner can use it. @@ -380,6 +386,7 @@ public final class DisplayInfo implements Parcelable { && appVsyncOffsetNanos == other.appVsyncOffsetNanos && presentationDeadlineNanos == other.presentationDeadlineNanos && state == other.state + && committedState == other.committedState && ownerUid == other.ownerUid && Objects.equals(ownerPackageName, other.ownerPackageName) && removeMode == other.removeMode @@ -431,6 +438,7 @@ public final class DisplayInfo implements Parcelable { appVsyncOffsetNanos = other.appVsyncOffsetNanos; presentationDeadlineNanos = other.presentationDeadlineNanos; state = other.state; + committedState = other.committedState; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; removeMode = other.removeMode; @@ -482,6 +490,7 @@ public final class DisplayInfo implements Parcelable { appVsyncOffsetNanos = source.readLong(); presentationDeadlineNanos = source.readLong(); state = source.readInt(); + committedState = source.readInt(); ownerUid = source.readInt(); ownerPackageName = source.readString8(); uniqueId = source.readString8(); @@ -538,6 +547,7 @@ public final class DisplayInfo implements Parcelable { dest.writeLong(appVsyncOffsetNanos); dest.writeLong(presentationDeadlineNanos); dest.writeInt(state); + dest.writeInt(committedState); dest.writeInt(ownerUid); dest.writeString8(ownerPackageName); dest.writeString8(uniqueId); @@ -761,6 +771,8 @@ public final class DisplayInfo implements Parcelable { sb.append(rotation); sb.append(", state "); sb.append(Display.stateToString(state)); + sb.append(", committedState "); + sb.append(Display.stateToString(committedState)); if (Process.myUid() != Process.SYSTEM_UID) { sb.append("}"); diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index 61098d60566f..3247bb7c6d38 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -19,6 +19,8 @@ package android.view; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.VisibleForTesting; @@ -49,6 +51,12 @@ import java.util.List; * @hide */ public class HandwritingInitiator { + /** The amount of extra space added to handwriting in dip. */ + private static final int HANDWRITING_AREA_PADDING_DIP = 20; + + /** The amount of extra space added to handwriting in px. */ + private final float mHandwritingAreaPaddingPx; + /** * The touchSlop from {@link ViewConfiguration} used to decide whether a pointer is considered * moving or stationary. @@ -88,9 +96,13 @@ public class HandwritingInitiator { @VisibleForTesting public HandwritingInitiator(@NonNull ViewConfiguration viewConfiguration, - @NonNull InputMethodManager inputMethodManager) { + @NonNull InputMethodManager inputMethodManager, DisplayMetrics displayMetrics) { mTouchSlop = viewConfiguration.getScaledTouchSlop(); mHandwritingTimeoutInMillis = ViewConfiguration.getLongPressTimeout(); + mHandwritingAreaPaddingPx = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + HANDWRITING_AREA_PADDING_DIP, + displayMetrics); mImm = inputMethodManager; } @@ -250,7 +262,8 @@ public class HandwritingInitiator { } final Rect handwritingArea = getViewHandwritingArea(connectedView); - if (contains(handwritingArea, mState.mStylusDownX, mState.mStylusDownY)) { + if (containsInExtendedHandwritingArea(handwritingArea, + mState.mStylusDownX, mState.mStylusDownY)) { startHandwriting(connectedView); } else { reset(); @@ -281,14 +294,21 @@ public class HandwritingInitiator { */ @Nullable private View findBestCandidateView(float x, float y) { + float minDistance = Float.MAX_VALUE; + View bestCandidate = null; + // If the connectedView is not null and do not set any handwriting area, it will check // whether the connectedView's boundary contains the initial stylus position. If true, // directly return the connectedView. final View connectedView = getConnectedView(); if (connectedView != null && connectedView.isAutoHandwritingEnabled()) { - final Rect handwritingArea = getViewHandwritingArea(connectedView); - if (contains(handwritingArea, x, y)) { - return connectedView; + Rect handwritingArea = getViewHandwritingArea(connectedView); + if (containsInExtendedHandwritingArea(handwritingArea, x, y)) { + final float distance = distance(handwritingArea, x, y); + if (distance == 0f) return connectedView; + + bestCandidate = connectedView; + minDistance = distance; } } @@ -297,18 +317,78 @@ public class HandwritingInitiator { mHandwritingAreasTracker.computeViewInfos(); for (HandwritableViewInfo viewInfo : handwritableViewInfos) { final View view = viewInfo.getView(); - if (!view.isAutoHandwritingEnabled()) continue; - if (contains(viewInfo.getHandwritingArea(), x, y)) { - return viewInfo.getView(); + final Rect handwritingArea = viewInfo.getHandwritingArea(); + if (!containsInExtendedHandwritingArea(handwritingArea, x, y)) continue; + + final float distance = distance(handwritingArea, x, y); + + if (distance == 0f) return view; + if (distance < minDistance) { + minDistance = distance; + bestCandidate = view; } } - return null; + return bestCandidate; + } + + /** + * Return the square of the distance from point (x, y) to the given rect, which is mainly used + * for comparison. The distance is defined to be: the shortest distance between (x, y) to any + * point on rect. When (x, y) is contained by the rect, return 0f. + */ + private static float distance(@NonNull Rect rect, float x, float y) { + if (contains(rect, x, y, 0f, 0f, 0f, 0f)) { + return 0f; + } + + /* The distance between point (x, y) and rect, there are 2 basic cases: + * a) The distance is the distance from (x, y) to the closest corner on rect. + * o | | + * ---+-----+--- + * | | + * ---+-----+--- + * | | + * b) The distance is the distance from (x, y) to the closest edge on rect. + * | o | + * ---+-----+--- + * | | + * ---+-----+--- + * | | + * We define xDistance as following(similar for yDistance): + * If x is in [left, right) 0, else min(abs(x - left), abs(x - y)) + * For case a, sqrt(xDistance^2 + yDistance^2) is the final distance. + * For case b, distance should be yDistance, which is also equal to + * sqrt(xDistance^2 + yDistance^2) because xDistance is 0. + */ + final float xDistance; + if (x >= rect.left && x < rect.right) { + xDistance = 0f; + } else if (x < rect.left) { + xDistance = rect.left - x; + } else { + xDistance = x - rect.right; + } + + final float yDistance; + if (y >= rect.top && y < rect.bottom) { + yDistance = 0f; + } else if (y < rect.top) { + yDistance = rect.top - y; + } else { + yDistance = y - rect.bottom; + } + // We can omit sqrt here because we only need the distance for comparison. + return xDistance * xDistance + yDistance * yDistance; } /** * Return the handwriting area of the given view, represented in the window's coordinate. * If the view didn't set any handwriting area, it will return the view's boundary. * It will return null if the view or its handwriting area is not visible. + * + * The handwriting area is clipped to its visible part. + * Notice that the returned rectangle is the view's original handwriting area without the + * view's handwriting area extends. */ @Nullable private static Rect getViewHandwritingArea(@NonNull View view) { @@ -329,11 +409,24 @@ public class HandwritingInitiator { } /** - * Return true if the (x, y) is inside by the given {@link Rect}. + * Return true if the (x, y) is inside by the given {@link Rect} extended by the View's + * handwriting extends settings. + */ + private boolean containsInExtendedHandwritingArea(@Nullable Rect handwritingArea, + float x, float y) { + if (handwritingArea == null) return false; + return contains(handwritingArea, x, y, mHandwritingAreaPaddingPx, mHandwritingAreaPaddingPx, + mHandwritingAreaPaddingPx, mHandwritingAreaPaddingPx); + } + + /** + * Return true if the (x, y) is inside by the given {@link Rect} extended by the given + * extendLeft, extendTop, extendRight and extendBottom. */ - private boolean contains(@Nullable Rect rect, float x, float y) { - if (rect == null) return false; - return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom; + private static boolean contains(@NonNull Rect rect, float x, float y, + float extendLeft, float extendTop, float extendRight, float extendBottom) { + return x >= rect.left - extendLeft && x < rect.right + extendRight + && y >= rect.top - extendTop && y < rect.bottom + extendBottom; } private boolean largerThanTouchSlop(float x1, float y1, float x2, float y2) { diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 7d5603994efa..addbab07c478 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -18,6 +18,7 @@ package android.view; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; @@ -517,6 +518,7 @@ public final class InputDevice implements Parcelable { * @param id The device id. * @return The input device or null if not found. */ + @Nullable public static InputDevice getDevice(int id) { return InputManager.getInstance().getInputDevice(id); } diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 2b79bbfa72d4..f838d7fed7cf 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -17,9 +17,9 @@ package android.view; import android.annotation.IntDef; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; -import android.sysprop.InputProperties; import android.util.ArrayMap; import android.util.Pools.SynchronizedPool; @@ -278,8 +278,10 @@ public final class VelocityTracker { private VelocityTracker(@VelocityTrackerStrategy int strategy) { // If user has not selected a specific strategy if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) { + final String strategyProperty = ViewConfiguration.get( + ActivityThread.currentActivityThread().getApplication()) + .getVelocityTrackerStrategy(); // Check if user specified strategy by overriding system property. - String strategyProperty = InputProperties.velocitytracker_strategy().orElse(null); if (strategyProperty == null || strategyProperty.isEmpty()) { mStrategy = strategy; } else { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 569461e81111..2d96c3e825a6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -31408,6 +31408,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link android.view.inputmethod.InputMethodManager#startStylusHandwriting(View)} when there * is stylus movement detected. * + * Note that this attribute has no effect on the View's children. For example, if a + * {@link ViewGroup} disables auto handwriting but its children set auto handwriting to true, + * auto handwriting will still work for the children, and vice versa. + * * @see #onCreateInputConnection(EditorInfo) * @see android.view.inputmethod.InputMethodManager#startStylusHandwriting(View) * @param enabled whether auto handwriting initiation is enabled for this view. diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index ebc409e470e9..30ae9c826f40 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; +import android.hardware.input.InputManager; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; @@ -348,6 +349,7 @@ public class ViewConfiguration { private final int mSmartSelectionInitializedTimeout; private final int mSmartSelectionInitializingTimeout; private final int mPreferKeepClearForFocusDelay; + private final String mVelocityTrackerStrategy; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768915) private boolean sHasPermanentMenuKey; @@ -394,6 +396,7 @@ public class ViewConfiguration { mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND; mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND; mPreferKeepClearForFocusDelay = -1; + mVelocityTrackerStrategy = InputManager.getInstance().getVelocityTrackerStrategy(); } /** @@ -510,6 +513,16 @@ public class ViewConfiguration { com.android.internal.R.integer.config_smartSelectionInitializingTimeoutMillis); mPreferKeepClearForFocusDelay = res.getInteger( com.android.internal.R.integer.config_preferKeepClearForFocusDelayMillis); + + mVelocityTrackerStrategy = InputManager.getInstance().getVelocityTrackerStrategy(); + } + + /** + * Get the current VelocityTracker strategy + * @hide + */ + public String getVelocityTrackerStrategy() { + return mVelocityTrackerStrategy; } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 27ce71155857..e7844d1c7f41 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -892,8 +892,10 @@ public final class ViewRootImpl implements ViewParent, mChoreographer = Choreographer.getInstance(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this)); - mHandwritingInitiator = new HandwritingInitiator(mViewConfiguration, - mContext.getSystemService(InputMethodManager.class)); + mHandwritingInitiator = new HandwritingInitiator( + mViewConfiguration, + mContext.getSystemService(InputMethodManager.class), + context.getResources().getDisplayMetrics()); String processorOverrideName = context.getResources().getString( R.string.config_inputEventCompatProcessorOverrideClassName); diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 90e349864092..ab17891a32dd 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -3063,6 +3063,9 @@ public class AccessibilityNodeInfo implements Parcelable { int spanToReplaceStart = spannable.getSpanStart(span); int spanToReplaceEnd = spannable.getSpanEnd(span); int spanToReplaceFlags = spannable.getSpanFlags(span); + if (spanToReplaceStart < 0) { + continue; + } spannable.removeSpan(span); ClickableSpan replacementSpan = (span instanceof URLSpan) ? new AccessibilityURLSpan((URLSpan) span) @@ -3100,6 +3103,9 @@ public class AccessibilityNodeInfo implements Parcelable { int spanToReplaceStart = spannable.getSpanStart(span); int spanToReplaceEnd = spannable.getSpanEnd(span); int spanToReplaceFlags = spannable.getSpanFlags(span); + if (spanToReplaceStart < 0) { + continue; + } spannable.removeSpan(span); ReplacementSpan replacementSpan = new AccessibilityReplacementSpan(replacementText); spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd, diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index b05b7916d960..92cf74519fbf 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -27,6 +27,7 @@ import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; import android.accessibilityservice.AccessibilityServiceInfo; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -47,17 +48,22 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.metrics.LogMaker; +import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; +import android.os.ICancellationSignal; import android.os.Looper; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.provider.DeviceConfig; import android.service.autofill.AutofillService; +import android.service.autofill.FillCallback; import android.service.autofill.FillEventHistory; +import android.service.autofill.IFillCallback; import android.service.autofill.UserData; import android.text.TextUtils; import android.util.ArrayMap; @@ -78,6 +84,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; +import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; @@ -102,6 +109,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.Executor; import sun.misc.Cleaner; @@ -170,6 +178,12 @@ import sun.misc.Cleaner; * shows an autofill save UI if the value of savable views have changed. If the user selects the * option to Save, the current value of the views is then sent to the autofill service. * + * <p>There is another choice for the application to provide it's datasets to the Autofill framework + * by setting an {@link AutofillRequestCallback} through + * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use + * its callback instead of the default {@link AutofillService}. See + * {@link AutofillRequestCallback} for more details. + * * <h3 id="additional-notes">Additional notes</h3> * * <p>It is safe to call <code>AutofillManager</code> methods from any thread. @@ -295,6 +309,7 @@ public final class AutofillManager { /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2; /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4; /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8; + /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20; // NOTE: flag below is used by the session start receiver only, hence it can have values above /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1; @@ -624,6 +639,11 @@ public final class AutofillManager { @GuardedBy("mLock") private boolean mEnabledForAugmentedAutofillOnly; + @GuardedBy("mLock") + @Nullable private AutofillRequestCallback mAutofillRequestCallback; + @GuardedBy("mLock") + @Nullable private Executor mRequestCallbackExecutor; + /** * Indicates whether there are any fields that need to do a fill request * after the activity starts. @@ -1952,6 +1972,32 @@ public final class AutofillManager { return new AutofillId(parent.getAutofillViewId(), virtualId); } + /** + * Sets the client's suggestions callback for autofill. + * + * @see AutofillRequestCallback + * + * @param executor specifies the thread upon which the callbacks will be invoked. + * @param callback which handles autofill request to provide client's suggestions. + */ + public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AutofillRequestCallback callback) { + synchronized (mLock) { + mRequestCallbackExecutor = executor; + mAutofillRequestCallback = callback; + } + } + + /** + * clears the client's suggestions callback for autofill. + */ + public void clearAutofillRequestCallback() { + synchronized (mLock) { + mRequestCallbackExecutor = null; + mAutofillRequestCallback = null; + } + } + @GuardedBy("mLock") private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, @NonNull AutofillValue value, int flags) { @@ -2012,6 +2058,13 @@ public final class AutofillManager { } } + if (mAutofillRequestCallback != null) { + if (sDebug) { + Log.d(TAG, "startSession with the client suggestions provider"); + } + flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS; + } + mService.startSession(client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), mCallback != null, flags, clientActivity, @@ -2365,6 +2418,28 @@ public final class AutofillManager { } } + private void onFillRequest(InlineSuggestionsRequest request, + CancellationSignal cancellationSignal, FillCallback callback) { + final AutofillRequestCallback autofillRequestCallback; + final Executor executor; + synchronized (mLock) { + autofillRequestCallback = mAutofillRequestCallback; + executor = mRequestCallbackExecutor; + } + if (autofillRequestCallback != null && executor != null) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> + autofillRequestCallback.onFillRequest( + request, cancellationSignal, callback)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } else { + callback.onSuccess(null); + } + } + /** @hide */ public static final int SET_STATE_FLAG_ENABLED = 0x01; /** @hide */ @@ -3852,6 +3927,23 @@ public final class AutofillManager { } } + @Override + public void requestFillFromClient(int id, InlineSuggestionsRequest request, + IFillCallback callback) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + ICancellationSignal transport = CancellationSignal.createTransport(); + try { + callback.onCancellable(transport); + } catch (RemoteException e) { + Slog.w(TAG, "Error requesting a cancellation", e); + } + + afm.onFillRequest(request, CancellationSignal.fromTransport(transport), + new FillCallback(callback, id)); + } + } + public void notifyFillDialogTriggerIds(List<AutofillId> ids) { final AutofillManager afm = mAfm.get(); if (afm != null) { diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java new file mode 100644 index 000000000000..e632a5849471 --- /dev/null +++ b/core/java/android/view/autofill/AutofillRequestCallback.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.autofill; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.CancellationSignal; +import android.service.autofill.FillCallback; +import android.view.inputmethod.InlineSuggestionsRequest; + +/** + * <p>This class is used to provide some input suggestions to the Autofill framework. + * + * <P>When the user is requested to input something, Autofill will try to query input suggestions + * for the user choosing. If the application want to provide some internal input suggestions, + * implements this callback and register via + * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor, + * AutofillRequestCallback)}. Autofill will callback the + * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request + * input suggestions. + * + * <P>To make sure the callback to take effect, must register before the autofill session starts. + * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current + * session, and then the callback will be used at the next restarted session. + * + * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch + * {@link AutofillId}s from its view structure. Below is an example: + * <pre class="prettyprint"> + * AutofillId usernameId = findViewById(R.id.username).getAutofillId(); + * AutofillId passwordId = findViewById(R.id.password).getAutofillId(); + * </pre> + * To learn more about creating a {@link android.service.autofill.FillResponse}, read + * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>. + * + * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond + * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill + * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback + * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the + * client would like to keep no suggestions for the field, respond with an empty + * {@link android.service.autofill.FillResponse} which has no dataset. + * + * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or + * the keyboard may choose to block your app from the inline strip. + */ +public interface AutofillRequestCallback { + /** + * Called by the Android system to decide if a screen can be autofilled by the callback. + * + * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if + * currently inline suggestions are supported and can be displayed. + * @param cancellationSignal signal for observing cancellation requests. The system will use + * this to notify you that the fill result is no longer needed and you should stop + * handling this fill request in order to save resources. + * @param callback object used to notify the result of the request. + */ + void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest, + @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback); +} diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 51afe4cf784d..2e5967cc32d1 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -24,9 +24,11 @@ import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; import android.os.IBinder; +import android.service.autofill.IFillCallback; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; +import android.view.inputmethod.InlineSuggestionsRequest; import android.view.KeyEvent; import com.android.internal.os.IResultReceiver; @@ -142,6 +144,12 @@ oneway interface IAutoFillManagerClient { void requestShowSoftInput(in AutofillId id); /** + * Requests to determine if a screen can be autofilled by the client app. + */ + void requestFillFromClient(int id, in InlineSuggestionsRequest request, + in IFillCallback callback); + + /** * Notifies autofill ids that require to show the fill dialog. */ void notifyFillDialogTriggerIds(in List<AutofillId> ids); diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index c78b810f0b1f..70279cc8e845 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -111,6 +111,22 @@ public final class InlineSuggestionsRequest implements Parcelable { private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec; /** + * Whether the IME supports inline suggestions from the default Autofill service that + * provides the input view. + * + * Note: The default value is {@code true}. + */ + private boolean mServiceSupported; + + /** + * Whether the IME supports inline suggestions from the application that provides the + * input view. + * + * Note: The default value is {@code true}. + */ + private boolean mClientSupported; + + /** * @hide * @see {@link #mHostInputToken}. */ @@ -204,6 +220,14 @@ public final class InlineSuggestionsRequest implements Parcelable { return Bundle.EMPTY; } + private static boolean defaultServiceSupported() { + return true; + } + + private static boolean defaultClientSupported() { + return true; + } + /** @hide */ abstract static class BaseBuilder { abstract Builder setInlinePresentationSpecs( @@ -216,15 +240,25 @@ public final class InlineSuggestionsRequest implements Parcelable { abstract Builder setHostDisplayId(int value); } + /** @hide */ + public boolean isServiceSupported() { + return mServiceSupported; + } + + /** @hide */ + public boolean isClientSupported() { + return mClientSupported; + } + - // Code below generated by codegen v1.0.23. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -240,7 +274,9 @@ public final class InlineSuggestionsRequest implements Parcelable { @NonNull Bundle extras, @Nullable IBinder hostInputToken, int hostDisplayId, - @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) { + @Nullable InlinePresentationSpec inlineTooltipPresentationSpec, + boolean serviceSupported, + boolean clientSupported) { this.mMaxSuggestionCount = maxSuggestionCount; this.mInlinePresentationSpecs = inlinePresentationSpecs; com.android.internal.util.AnnotationValidations.validate( @@ -257,6 +293,8 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mHostInputToken = hostInputToken; this.mHostDisplayId = hostDisplayId; this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec; + this.mServiceSupported = serviceSupported; + this.mClientSupported = clientSupported; onConstructed(); } @@ -340,7 +378,9 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * Specifies the UI specification for the inline suggestion tooltip in the response. + * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response. + * + * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec) */ @DataClass.Generated.Member public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() { @@ -361,7 +401,9 @@ public final class InlineSuggestionsRequest implements Parcelable { "extras = " + mExtras + ", " + "hostInputToken = " + mHostInputToken + ", " + "hostDisplayId = " + mHostDisplayId + ", " + - "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + + "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " + + "serviceSupported = " + mServiceSupported + ", " + + "clientSupported = " + mClientSupported + " }"; } @@ -385,7 +427,9 @@ public final class InlineSuggestionsRequest implements Parcelable { && extrasEquals(that.mExtras) && java.util.Objects.equals(mHostInputToken, that.mHostInputToken) && mHostDisplayId == that.mHostDisplayId - && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec); + && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec) + && mServiceSupported == that.mServiceSupported + && mClientSupported == that.mClientSupported; } @Override @@ -403,6 +447,8 @@ public final class InlineSuggestionsRequest implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken); _hash = 31 * _hash + mHostDisplayId; _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec); + _hash = 31 * _hash + Boolean.hashCode(mServiceSupported); + _hash = 31 * _hash + Boolean.hashCode(mClientSupported); return _hash; } @@ -413,6 +459,8 @@ public final class InlineSuggestionsRequest implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } int flg = 0; + if (mServiceSupported) flg |= 0x100; + if (mClientSupported) flg |= 0x200; if (mHostInputToken != null) flg |= 0x20; if (mInlineTooltipPresentationSpec != null) flg |= 0x80; dest.writeInt(flg); @@ -438,6 +486,8 @@ public final class InlineSuggestionsRequest implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } int flg = in.readInt(); + boolean serviceSupported = (flg & 0x100) != 0; + boolean clientSupported = (flg & 0x200) != 0; int maxSuggestionCount = in.readInt(); List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>(); in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader(), android.widget.inline.InlinePresentationSpec.class); @@ -464,6 +514,8 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mHostInputToken = hostInputToken; this.mHostDisplayId = hostDisplayId; this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec; + this.mServiceSupported = serviceSupported; + this.mClientSupported = clientSupported; onConstructed(); } @@ -497,6 +549,8 @@ public final class InlineSuggestionsRequest implements Parcelable { private @Nullable IBinder mHostInputToken; private int mHostDisplayId; private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec; + private boolean mServiceSupported; + private boolean mClientSupported; private long mBuilderFieldsSet = 0L; @@ -629,7 +683,9 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * Specifies the UI specification for the inline suggestion tooltip in the response. + * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response. + * + * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s */ @DataClass.Generated.Member public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) { @@ -639,10 +695,38 @@ public final class InlineSuggestionsRequest implements Parcelable { return this; } + /** + * Whether the IME supports inline suggestions from the default Autofill service that + * provides the input view. + * + * Note: The default value is {@code true}. + */ + @DataClass.Generated.Member + public @NonNull Builder setServiceSupported(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x100; + mServiceSupported = value; + return this; + } + + /** + * Whether the IME supports inline suggestions from the application that provides the + * input view. + * + * Note: The default value is {@code true}. + */ + @DataClass.Generated.Member + public @NonNull Builder setClientSupported(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x200; + mClientSupported = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull InlineSuggestionsRequest build() { checkNotUsed(); - mBuilderFieldsSet |= 0x100; // Mark builder used + mBuilderFieldsSet |= 0x400; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mMaxSuggestionCount = defaultMaxSuggestionCount(); @@ -665,6 +749,12 @@ public final class InlineSuggestionsRequest implements Parcelable { if ((mBuilderFieldsSet & 0x80) == 0) { mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec(); } + if ((mBuilderFieldsSet & 0x100) == 0) { + mServiceSupported = defaultServiceSupported(); + } + if ((mBuilderFieldsSet & 0x200) == 0) { + mClientSupported = defaultClientSupported(); + } InlineSuggestionsRequest o = new InlineSuggestionsRequest( mMaxSuggestionCount, mInlinePresentationSpecs, @@ -673,12 +763,14 @@ public final class InlineSuggestionsRequest implements Parcelable { mExtras, mHostInputToken, mHostDisplayId, - mInlineTooltipPresentationSpec); + mInlineTooltipPresentationSpec, + mServiceSupported, + mClientSupported); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x100) != 0) { + if ((mBuilderFieldsSet & 0x400) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -686,10 +778,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1621415989607L, - codegenVersion = "1.0.23", + time = 1615798784918L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 805f8e7551a5..52d88d6f2906 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2918,7 +2918,8 @@ public final class InputMethodManager { */ @Deprecated public void hideSoftInputFromInputMethod(IBinder token, int flags) { - InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags); + InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput( + flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION); } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 231ae084dd6c..184e7bca963b 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -4363,8 +4363,35 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final int delta = Math.round(axisValue * mVerticalScrollFactor); if (delta != 0) { + // If we're moving down, we want the top item. If we're moving up, bottom item. + final int motionIndex = delta > 0 ? 0 : getChildCount() - 1; + + int motionViewPrevTop = 0; + View motionView = this.getChildAt(motionIndex); + if (motionView != null) { + motionViewPrevTop = motionView.getTop(); + } + + final int overscrollMode = getOverScrollMode(); + if (!trackMotionScroll(delta, delta)) { return true; + } else if (!event.isFromSource(InputDevice.SOURCE_MOUSE) && motionView != null + && (overscrollMode == OVER_SCROLL_ALWAYS + || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS + && !contentFits()))) { + int motionViewRealTop = motionView.getTop(); + float overscroll = (delta - (motionViewRealTop - motionViewPrevTop)) + / ((float) getHeight()); + if (delta > 0) { + mEdgeGlowTop.onPullDistance(overscroll, 0.5f); + mEdgeGlowTop.onRelease(); + } else { + mEdgeGlowBottom.onPullDistance(-overscroll, 0.5f); + mEdgeGlowBottom.onRelease(); + } + invalidate(); + return true; } } break; diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 018cba7f95e5..2dbfd7e5b2e2 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -872,15 +872,39 @@ public class HorizontalScrollView extends FrameLayout { final int range = getScrollRange(); int oldScrollX = mScrollX; int newScrollX = oldScrollX + delta; + + final int overscrollMode = getOverScrollMode(); + boolean canOverscroll = !event.isFromSource(InputDevice.SOURCE_MOUSE) + && (overscrollMode == OVER_SCROLL_ALWAYS + || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)); + boolean absorbed = false; + if (newScrollX < 0) { + if (canOverscroll) { + mEdgeGlowLeft.onPullDistance(-(float) newScrollX / getWidth(), + 0.5f); + mEdgeGlowLeft.onRelease(); + invalidate(); + absorbed = true; + } newScrollX = 0; } else if (newScrollX > range) { + if (canOverscroll) { + mEdgeGlowRight.onPullDistance( + (float) (newScrollX - range) / getWidth(), 0.5f); + mEdgeGlowRight.onRelease(); + invalidate(); + absorbed = true; + } newScrollX = range; } if (newScrollX != oldScrollX) { super.scrollTo(newScrollX, mScrollY); return true; } + if (absorbed) { + return true; + } } } } diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 8e293f4b356d..61a7599e8f73 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -52,9 +52,9 @@ import android.widget.RemoteViews.InteractionHandler; import com.android.internal.widget.IRemoteViewsFactory; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedList; import java.util.concurrent.Executor; /** @@ -424,17 +424,17 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback * adapter that have not yet had their RemoteViews loaded. */ private class RemoteViewsFrameLayoutRefSet - extends SparseArray<LinkedList<RemoteViewsFrameLayout>> { + extends SparseArray<ArrayList<RemoteViewsFrameLayout>> { /** * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. */ public void add(int position, RemoteViewsFrameLayout layout) { - LinkedList<RemoteViewsFrameLayout> refs = get(position); + ArrayList<RemoteViewsFrameLayout> refs = get(position); // Create the list if necessary if (refs == null) { - refs = new LinkedList<>(); + refs = new ArrayList<>(); put(position, refs); } @@ -451,7 +451,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback if (view == null) return; // Remove this set from the original mapping - final LinkedList<RemoteViewsFrameLayout> refs = removeReturnOld(position); + final ArrayList<RemoteViewsFrameLayout> refs = removeReturnOld(position); if (refs != null) { // Notify all the references for that position of the newly loaded RemoteViews for (final RemoteViewsFrameLayout ref : refs) { @@ -467,7 +467,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback if (rvfl.cacheIndex < 0) { return; } - final LinkedList<RemoteViewsFrameLayout> refs = get(rvfl.cacheIndex); + final ArrayList<RemoteViewsFrameLayout> refs = get(rvfl.cacheIndex); if (refs != null) { refs.remove(rvfl); } @@ -933,12 +933,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback Runnable r = () -> { synchronized (sCachedRemoteViewsCaches) { - if (sCachedRemoteViewsCaches.containsKey(key)) { - sCachedRemoteViewsCaches.remove(key); - } - if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { - sRemoteViewsCacheRemoveRunnables.remove(key); - } + sCachedRemoteViewsCaches.remove(key); + sRemoteViewsCacheRemoveRunnables.remove(key); } }; sRemoteViewsCacheRemoveRunnables.put(key, r); diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 15cd17b20f4f..2acd50c9e169 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -939,15 +939,38 @@ public class ScrollView extends FrameLayout { final int range = getScrollRange(); int oldScrollY = mScrollY; int newScrollY = oldScrollY - delta; + + final int overscrollMode = getOverScrollMode(); + boolean canOverscroll = !event.isFromSource(InputDevice.SOURCE_MOUSE) + && (overscrollMode == OVER_SCROLL_ALWAYS + || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)); + boolean absorbed = false; + if (newScrollY < 0) { + if (canOverscroll) { + mEdgeGlowTop.onPullDistance(-(float) newScrollY / getHeight(), 0.5f); + mEdgeGlowTop.onRelease(); + invalidate(); + absorbed = true; + } newScrollY = 0; } else if (newScrollY > range) { + if (canOverscroll) { + mEdgeGlowBottom.onPullDistance( + (float) (newScrollY - range) / getHeight(), 0.5f); + mEdgeGlowBottom.onRelease(); + invalidate(); + absorbed = true; + } newScrollY = range; } if (newScrollY != oldScrollY) { super.scrollTo(mScrollX, newScrollY); return true; } + if (absorbed) { + return true; + } } break; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 54c0d7c9af32..9e1bc8f54212 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -144,6 +144,7 @@ import android.text.style.UpdateAppearance; import android.text.util.Linkify; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.FeatureFlagUtils; import android.util.IntArray; import android.util.Log; import android.util.SparseIntArray; @@ -791,6 +792,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE; private int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE; + // The auto option for LINE_BREAK_WORD_STYLE_PHRASE may not be applied in recycled view due to + // one-way flag flipping. This is a tentative limitation during experiment and will not have the + // issue once this is finalized to LINE_BREAK_WORD_STYLE_PHRASE_AUTO option. + private boolean mUserSpeficiedLineBreakwordStyle = false; + // This is used to reflect the current user preference for changing font weight and making text // more bold. private int mFontWeightAdjustment; @@ -1462,6 +1468,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_lineBreakWordStyle: + if (a.hasValue(attr)) { + mUserSpeficiedLineBreakwordStyle = true; + } mLineBreakWordStyle = a.getInt(attr, LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE); break; @@ -4209,6 +4218,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle: attributes.mHasLineBreakWordStyle = true; + mUserSpeficiedLineBreakwordStyle = true; attributes.mLineBreakWordStyle = appearance.getInt(attr, attributes.mLineBreakWordStyle); break; @@ -4910,6 +4920,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @param lineBreakWordStyle the line break word style for the tet */ public void setLineBreakWordStyle(@LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) { + mUserSpeficiedLineBreakwordStyle = true; if (mLineBreakWordStyle != lineBreakWordStyle) { mLineBreakWordStyle = lineBreakWordStyle; if (mLayout != null) { @@ -4945,8 +4956,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see PrecomputedText */ public @NonNull PrecomputedText.Params getTextMetricsParams() { + final boolean autoPhraseBreaking = + !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING); return new PrecomputedText.Params(new TextPaint(mTextPaint), - LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle), + LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle, + autoPhraseBreaking), getTextDirectionHeuristic(), mBreakStrategy, mHyphenationFrequency); } @@ -4966,6 +4981,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener LineBreakConfig lineBreakConfig = params.getLineBreakConfig(); mLineBreakStyle = lineBreakConfig.getLineBreakStyle(); mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle(); + mUserSpeficiedLineBreakwordStyle = true; if (mLayout != null) { nullLayouts(); requestLayout(); @@ -6502,10 +6518,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mTextDir == null) { mTextDir = getTextDirectionHeuristic(); } + final boolean autoPhraseBreaking = + !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING); final @PrecomputedText.Params.CheckResultUsableResult int checkResult = precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy, mHyphenationFrequency, LineBreakConfig.getLineBreakConfig( - mLineBreakStyle, mLineBreakWordStyle)); + mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking)); switch (checkResult) { case PrecomputedText.Params.UNUSABLE: throw new IllegalArgumentException( @@ -9391,6 +9410,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // TODO: code duplication with makeSingleLayout() if (mHintLayout == null) { + final boolean autoPhraseBreaking = + !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING); StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0, mHint.length(), mTextPaint, hintWidth) .setAlignment(alignment) @@ -9403,7 +9425,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setJustificationMode(mJustificationMode) .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE) .setLineBreakConfig(LineBreakConfig.getLineBreakConfig( - mLineBreakStyle, mLineBreakWordStyle)); + mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking)); if (shouldEllipsize) { builder.setEllipsize(mEllipsize) .setEllipsizedWidth(ellipsisWidth); @@ -9507,6 +9529,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (result == null) { + final boolean autoPhraseBreaking = + !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING); StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed, 0, mTransformed.length(), mTextPaint, wantWidth) .setAlignment(alignment) @@ -9519,7 +9544,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setJustificationMode(mJustificationMode) .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE) .setLineBreakConfig(LineBreakConfig.getLineBreakConfig( - mLineBreakStyle, mLineBreakWordStyle)); + mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking)); if (shouldEllipsize) { builder.setEllipsize(effectiveEllipsize) .setEllipsizedWidth(ellipsisWidth); @@ -9877,7 +9902,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain( text, 0, text.length(), mTempTextPaint, Math.round(availableSpace.right)); - + final boolean autoPhraseBreaking = + !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING); layoutBuilder.setAlignment(getLayoutAlignment()) .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier()) .setIncludePad(getIncludeFontPadding()) @@ -9888,7 +9915,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE) .setTextDirection(getTextDirectionHeuristic()) .setLineBreakConfig(LineBreakConfig.getLineBreakConfig( - mLineBreakStyle, mLineBreakWordStyle)); + mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking)); final StaticLayout layout = layoutBuilder.build(); diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java index 42fc7bd6e6fc..975954035c17 100644 --- a/core/java/com/android/internal/app/AbstractResolverComparator.java +++ b/core/java/com/android/internal/app/AbstractResolverComparator.java @@ -228,12 +228,6 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC */ abstract float getScore(ComponentName name); - /** - * Returns the list of top K component names which have highest - * {@link #getScore(ComponentName)} - */ - abstract List<ComponentName> getTopComponentNames(int topK); - /** Handles result message sent to mHandler. */ abstract void handleResultMessage(Message message); diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java index bc9eff04636d..b19ac2fec640 100644 --- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java +++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java @@ -18,6 +18,7 @@ package com.android.internal.app; import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH; +import android.annotation.Nullable; import android.app.prediction.AppPredictor; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; @@ -33,12 +34,11 @@ import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.Executors; -import java.util.stream.Collectors; /** * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be @@ -58,7 +58,9 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator private final String mReferrerPackage; // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall // back to using the ResolverRankerService. + // TODO: responsibility for this fallback behavior can live outside of the AppPrediction client. private ResolverRankerServiceResolverComparator mResolverRankerService; + private AppPredictionServiceComparatorModel mComparatorModel; AppPredictionServiceResolverComparator( Context context, @@ -74,25 +76,12 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator mUser = user; mReferrerPackage = referrerPackage; setChooserActivityLogger(chooserActivityLogger); + mComparatorModel = buildUpdatedModel(); } @Override int compare(ResolveInfo lhs, ResolveInfo rhs) { - if (mResolverRankerService != null) { - return mResolverRankerService.compare(lhs, rhs); - } - Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName, - lhs.activityInfo.name)); - Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName, - rhs.activityInfo.name)); - if (lhsRank == null && rhsRank == null) { - return 0; - } else if (lhsRank == null) { - return -1; - } else if (rhsRank == null) { - return 1; - } - return lhsRank - rhsRank; + return mComparatorModel.getComparator().compare(lhs, rhs); } @Override @@ -121,6 +110,7 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator mContext, mIntent, mReferrerPackage, () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT), getChooserActivityLogger()); + mComparatorModel = buildUpdatedModel(); mResolverRankerService.compute(targets); } else { Log.i(TAG, "AppPredictionService response received"); @@ -163,6 +153,7 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator mTargetRanks.put(componentName, i); Log.i(TAG, "handleSortedAppTargets, sortedAppTargets #" + i + ": " + componentName); } + mComparatorModel = buildUpdatedModel(); } private boolean checkAppTargetRankValid(List<AppTarget> sortedAppTargets) { @@ -176,43 +167,12 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator @Override float getScore(ComponentName name) { - if (mResolverRankerService != null) { - return mResolverRankerService.getScore(name); - } - Integer rank = mTargetRanks.get(name); - if (rank == null) { - Log.w(TAG, "Score requested for unknown component. Did you call compute yet?"); - return 0f; - } - int consecutiveSumOfRanks = (mTargetRanks.size() - 1) * (mTargetRanks.size()) / 2; - return 1.0f - (((float) rank) / consecutiveSumOfRanks); - } - - @Override - List<ComponentName> getTopComponentNames(int topK) { - if (mResolverRankerService != null) { - return mResolverRankerService.getTopComponentNames(topK); - } - return mTargetRanks.entrySet().stream() - .sorted(Entry.comparingByValue()) - .limit(topK) - .map(Entry::getKey) - .collect(Collectors.toList()); + return mComparatorModel.getScore(name); } @Override void updateModel(ComponentName componentName) { - if (mResolverRankerService != null) { - mResolverRankerService.updateModel(componentName); - return; - } - mAppPredictor.notifyAppTargetEvent( - new AppTargetEvent.Builder( - new AppTarget.Builder( - new AppTargetId(componentName.toString()), - componentName.getPackageName(), mUser) - .setClassName(componentName.getClassName()).build(), - ACTION_LAUNCH).build()); + mComparatorModel.notifyOnTargetSelected(componentName); } @Override @@ -220,6 +180,97 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator if (mResolverRankerService != null) { mResolverRankerService.destroy(); mResolverRankerService = null; + mComparatorModel = buildUpdatedModel(); + } + } + + /** + * Re-construct an {@code AppPredictionServiceComparatorModel} to replace the current model + * instance (if any) using the up-to-date {@code AppPredictionServiceResolverComparator} ivar + * values. + * + * TODO: each time we replace the model instance, we're either updating the model to use + * adjusted data (which is appropriate), or we're providing a (late) value for one of our ivars + * that wasn't available the last time the model was updated. For those latter cases, we should + * just avoid creating the model altogether until we have all the prerequisites we'll need. Then + * we can probably simplify the logic in {@code AppPredictionServiceComparatorModel} since we + * won't need to handle edge cases when the model data isn't fully prepared. + * (In some cases, these kinds of "updates" might interleave -- e.g., we might have finished + * initializing the first time and now want to adjust some data, but still need to wait for + * changes to propagate to the other ivars before rebuilding the model.) + */ + private AppPredictionServiceComparatorModel buildUpdatedModel() { + return new AppPredictionServiceComparatorModel( + mAppPredictor, mResolverRankerService, mUser, mTargetRanks); + } + + // TODO: Finish separating behaviors of AbstractResolverComparator, then (probably) make this a + // standalone class once clients are written in terms of ResolverComparatorModel. + static class AppPredictionServiceComparatorModel implements ResolverComparatorModel { + private final AppPredictor mAppPredictor; + private final ResolverRankerServiceResolverComparator mResolverRankerService; + private final UserHandle mUser; + private final Map<ComponentName, Integer> mTargetRanks; // Treat as immutable. + + AppPredictionServiceComparatorModel( + AppPredictor appPredictor, + @Nullable ResolverRankerServiceResolverComparator resolverRankerService, + UserHandle user, + Map<ComponentName, Integer> targetRanks) { + mAppPredictor = appPredictor; + mResolverRankerService = resolverRankerService; + mUser = user; + mTargetRanks = targetRanks; + } + + @Override + public Comparator<ResolveInfo> getComparator() { + return (lhs, rhs) -> { + if (mResolverRankerService != null) { + return mResolverRankerService.compare(lhs, rhs); + } + Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName, + lhs.activityInfo.name)); + Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName, + rhs.activityInfo.name)); + if (lhsRank == null && rhsRank == null) { + return 0; + } else if (lhsRank == null) { + return -1; + } else if (rhsRank == null) { + return 1; + } + return lhsRank - rhsRank; + }; + } + + @Override + public float getScore(ComponentName name) { + if (mResolverRankerService != null) { + return mResolverRankerService.getScore(name); + } + Integer rank = mTargetRanks.get(name); + if (rank == null) { + Log.w(TAG, "Score requested for unknown component. Did you call compute yet?"); + return 0f; + } + int consecutiveSumOfRanks = (mTargetRanks.size() - 1) * (mTargetRanks.size()) / 2; + return 1.0f - (((float) rank) / consecutiveSumOfRanks); + } + + @Override + public void notifyOnTargetSelected(ComponentName componentName) { + if (mResolverRankerService != null) { + mResolverRankerService.updateModel(componentName); + return; + } + mAppPredictor.notifyAppTargetEvent( + new AppTargetEvent.Builder( + new AppTarget.Builder( + new AppTargetId(componentName.toString()), + componentName.getPackageName(), mUser) + .setClassName(componentName.getClassName()).build(), + ACTION_LAUNCH).build()); } } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index d4a8a164803f..595c9634f8ba 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -194,9 +194,6 @@ public class ChooserActivity extends ResolverActivity implements private static final String PLURALS_COUNT = "count"; private static final String PLURALS_FILE_NAME = "file_name"; - @VisibleForTesting - public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250; - private boolean mIsAppPredictorComponentAvailable; private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache; private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache; @@ -248,6 +245,13 @@ public class ChooserActivity extends ResolverActivity implements SystemUiDeviceConfigFlags.IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP, DEFAULT_IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP); + private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 250; + + @VisibleForTesting + int mListViewUpdateDelayMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SHARESHEET_LIST_VIEW_UPDATE_DELAY, + DEFAULT_LIST_VIEW_UPDATE_DELAY_MS); + private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; private IntentSender mRefinementIntentSender; @@ -2605,7 +2609,7 @@ public class ChooserActivity extends ResolverActivity implements Message msg = Message.obtain(); msg.what = ChooserHandler.LIST_VIEW_UPDATE_MESSAGE; msg.obj = userHandle; - mChooserHandler.sendMessageDelayed(msg, LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + mChooserHandler.sendMessageDelayed(msg, mListViewUpdateDelayMs); } @Override diff --git a/core/java/com/android/internal/app/ChooserUtil.java b/core/java/com/android/internal/app/ChooserUtil.java deleted file mode 100644 index 3f8788cba9b9..000000000000 --- a/core/java/com/android/internal/app/ChooserUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.app; - -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** - * Utility method for common computation operations for Share sheet. - */ -public class ChooserUtil { - - private static final Charset UTF_8 = Charset.forName("UTF-8"); - - /** - * Hashes the given input based on MD5 algorithm. - * - * @return a string representation of the hash computation. - */ - public static String md5(String input) { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - md.update(input.getBytes(UTF_8)); - return convertBytesToHexString(md.digest()); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException(e); - } - } - - /** Converts byte array input into an hex string. */ - private static String convertBytesToHexString(byte[] input) { - char[] chars = new char[input.length * 2]; - for (int i = 0; i < input.length; i++) { - byte b = input[i]; - chars[i * 2] = Character.forDigit((b >> 4) & 0xF, 16 /* radix */); - chars[i * 2 + 1] = Character.forDigit(b & 0xF, 16 /* radix */); - } - return new String(chars); - } - - private ChooserUtil() {} -} diff --git a/core/java/com/android/internal/app/ResolverComparatorModel.java b/core/java/com/android/internal/app/ResolverComparatorModel.java new file mode 100644 index 000000000000..3e8f64bf4ed3 --- /dev/null +++ b/core/java/com/android/internal/app/ResolverComparatorModel.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.content.ComponentName; +import android.content.pm.ResolveInfo; + +import java.util.Comparator; +import java.util.List; + +/** + * A ranking model for resolver targets, providing ordering and (optionally) numerical scoring. + * + * As required by the {@link Comparator} contract, objects returned by {@code getComparator()} must + * apply a total ordering on its inputs consistent across all calls to {@code Comparator#compare()}. + * Other query methods and ranking feedback should refer to that same ordering, so implementors are + * generally advised to "lock in" an immutable snapshot of their model data when this object is + * initialized (preferring to replace the entire {@code ResolverComparatorModel} instance if the + * backing data needs to be updated in the future). + */ +interface ResolverComparatorModel { + /** + * Get a {@code Comparator} that can be used to sort {@code ResolveInfo} targets according to + * the model ranking. + */ + Comparator<ResolveInfo> getComparator(); + + /** + * Get the numerical score, if any, that the model assigns to the component with the specified + * {@code name}. Scores range from zero to one, with one representing the highest possible + * likelihood that the user will select that component as the target. Implementations that don't + * assign numerical scores are <em>recommended</em> to return a value of 0 for all components. + */ + float getScore(ComponentName name); + + /** + * Notify the model that the user selected a target. (Models may log this information, use it as + * a feedback signal for their ranking, etc.) Because the data in this + * {@code ResolverComparatorModel} instance is immutable, clients will need to get an up-to-date + * instance in order to see any changes in the ranking that might result from this feedback. + */ + void notifyOnTargetSelected(ComponentName componentName); +} diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index ac9b2d8a8d8f..351ac4587def 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -155,14 +155,6 @@ public class ResolverListAdapter extends BaseAdapter { return mResolverListController.getScore(componentName); } - /** - * Returns the list of top K component names which have highest - * {@link #getScore(DisplayResolveInfo)} - */ - public List<ComponentName> getTopComponentNames(int topK) { - return mResolverListController.getTopComponentNames(topK); - } - public void updateModel(ComponentName componentName) { mResolverListController.updateModel(componentName); } @@ -177,107 +169,206 @@ public class ResolverListAdapter extends BaseAdapter { } /** - * Rebuild the list of resolvers. In some cases some parts will need some asynchronous work - * to complete. + * Rebuild the list of resolvers. When rebuilding is complete, queue the {@code onPostListReady} + * callback on the main handler with {@code rebuildCompleted} true. + * + * In some cases some parts will need some asynchronous work to complete. Then this will first + * immediately queue {@code onPostListReady} (on the main handler) with {@code rebuildCompleted} + * false; only when the asynchronous work completes will this then go on to queue another + * {@code onPostListReady} callback with {@code rebuildCompleted} true. * - * The {@code doPostProcessing } parameter is used to specify whether to update the UI and - * load additional targets (e.g. direct share) after the list has been rebuilt. This is used - * in the case where we want to load the inactive profile's resolved apps to know the + * The {@code doPostProcessing} parameter is used to specify whether to update the UI and + * load additional targets (e.g. direct share) after the list has been rebuilt. We may choose + * to skip that step if we're only loading the inactive profile's resolved apps to know the * number of targets. * - * @return Whether or not the list building is completed. + * @return Whether the list building was completed synchronously. If not, we'll queue the + * {@code onPostListReady} callback first with {@code rebuildCompleted} false, and then again + * with {@code rebuildCompleted} true at the end of some newly-launched asynchronous work. + * Otherwise the callback is only queued once, with {@code rebuildCompleted} true. */ protected boolean rebuildList(boolean doPostProcessing) { - List<ResolvedComponentInfo> currentResolveList = null; - // Clear the value of mOtherProfile from previous call. - mOtherProfile = null; - mLastChosen = null; - mLastChosenPosition = -1; mDisplayList.clear(); mIsTabLoaded = false; + mLastChosenPosition = -1; + + List<ResolvedComponentInfo> currentResolveList = getInitialRebuiltResolveList(); + + /* TODO: this seems like unnecessary extra complexity; why do we need to do this "primary" + * (i.e. "eligibility") filtering before evaluating the "other profile" special-treatment, + * but the "secondary" (i.e. "priority") filtering after? Are there in fact cases where the + * eligibility conditions will filter out a result that would've otherwise gotten the "other + * profile" treatment? Or, are there cases where the priority conditions *would* filter out + * a result, but we *want* that result to get the "other profile" treatment, so we only + * filter *after* evaluating the special-treatment conditions? If the answer to either is + * "no," then the filtering steps can be consolidated. (And that also makes the "unfiltered + * list" bookkeeping a little cleaner.) + */ + mUnfilteredResolveList = performPrimaryResolveListFiltering(currentResolveList); + // So far we only support a single other profile at a time. + // The first one we see gets special treatment. + ResolvedComponentInfo otherProfileInfo = + getFirstNonCurrentUserResolvedComponentInfo(currentResolveList); + updateOtherProfileTreatment(otherProfileInfo); + if (otherProfileInfo != null) { + currentResolveList.remove(otherProfileInfo); + /* TODO: the previous line removed the "other profile info" item from + * mUnfilteredResolveList *ONLY IF* that variable is an alias for the same List instance + * as currentResolveList (i.e., if no items were filtered out as the result of the + * earlier "primary" filtering). It seems wrong for our behavior to depend on that. + * Should we: + * A. replicate the above removal to mUnfilteredResolveList (which is idempotent, so we + * don't even have to check whether they're aliases); or + * B. break the alias relationship by copying currentResolveList to a new + * mUnfilteredResolveList instance if necessary before removing otherProfileInfo? + * In other words: do we *want* otherProfileInfo in the "unfiltered" results? Either + * way, we'll need one of the changes suggested above. + */ + } + + // If no results have yet been filtered, mUnfilteredResolveList is an alias for the same + // List instance as currentResolveList. Then we need to make a copy to store as the + // mUnfilteredResolveList if we go on to filter any more items. Otherwise we've already + // copied the original unfiltered items to a separate List instance and can now filter + // the remainder in-place without any further bookkeeping. + boolean needsCopyOfUnfiltered = (mUnfilteredResolveList == currentResolveList); + mUnfilteredResolveList = performSecondaryResolveListFiltering( + currentResolveList, needsCopyOfUnfiltered); + + return finishRebuildingListWithFilteredResults(currentResolveList, doPostProcessing); + } + + /** + * Get the full (unfiltered) set of {@code ResolvedComponentInfo} records for all resolvers + * to be considered in a newly-rebuilt list. This list will be filtered and ranked before the + * rebuild is complete. + */ + List<ResolvedComponentInfo> getInitialRebuiltResolveList() { if (mBaseResolveList != null) { - currentResolveList = mUnfilteredResolveList = new ArrayList<>(); + List<ResolvedComponentInfo> currentResolveList = new ArrayList<>(); mResolverListController.addResolveListDedupe(currentResolveList, mResolverListCommunicator.getTargetIntent(), mBaseResolveList); + return currentResolveList; } else { - currentResolveList = mUnfilteredResolveList = - mResolverListController.getResolversForIntent( + return mResolverListController.getResolversForIntent( /* shouldGetResolvedFilter= */ true, mResolverListCommunicator.shouldGetActivityMetadata(), mIntents); - if (currentResolveList == null) { - processSortedList(currentResolveList, doPostProcessing); - return true; - } - List<ResolvedComponentInfo> originalList = - mResolverListController.filterIneligibleActivities(currentResolveList, - true); - if (originalList != null) { - mUnfilteredResolveList = originalList; - } } + } - // So far we only support a single other profile at a time. - // The first one we see gets special treatment. - for (ResolvedComponentInfo info : currentResolveList) { - ResolveInfo resolveInfo = info.getResolveInfoAt(0); - if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) { - Intent pOrigIntent = mResolverListCommunicator.getReplacementIntent( - resolveInfo.activityInfo, - info.getIntentAt(0)); - Intent replacementIntent = mResolverListCommunicator.getReplacementIntent( - resolveInfo.activityInfo, - mResolverListCommunicator.getTargetIntent()); - mOtherProfile = new DisplayResolveInfo(info.getIntentAt(0), - resolveInfo, - resolveInfo.loadLabel(mPm), - resolveInfo.loadLabel(mPm), - pOrigIntent != null ? pOrigIntent : replacementIntent, - makePresentationGetter(resolveInfo)); - currentResolveList.remove(info); - break; - } + /** + * Remove ineligible activities from {@code currentResolveList} (if non-null), in-place. More + * broadly, filtering logic should apply in the "primary" stage if it should preclude items from + * receiving the "other profile" special-treatment described in {@code rebuildList()}. + * + * @return A copy of the original {@code currentResolveList}, if any items were removed, or a + * (possibly null) reference to the original list otherwise. (That is, this always returns a + * list of all the unfiltered items, but if no items were filtered, it's just an alias for the + * same list that was passed in). + */ + @Nullable + List<ResolvedComponentInfo> performPrimaryResolveListFiltering( + @Nullable List<ResolvedComponentInfo> currentResolveList) { + /* TODO: mBaseResolveList appears to be(?) some kind of configured mode. Why is it not + * subject to filterIneligibleActivities, even though all the other logic still applies + * (including "secondary" filtering)? (This also relates to the earlier question; do we + * believe there's an item that would be eligible for "other profile" special treatment, + * except we want to filter it out as ineligible... but only if we're not in + * "mBaseResolveList mode"? */ + if ((mBaseResolveList != null) || (currentResolveList == null)) { + return currentResolveList; + } + + List<ResolvedComponentInfo> originalList = + mResolverListController.filterIneligibleActivities(currentResolveList, true); + return (originalList == null) ? currentResolveList : originalList; + } + + /** + * Remove low-priority activities from {@code currentResolveList} (if non-null), in place. More + * broadly, filtering logic should apply in the "secondary" stage to prevent items from + * appearing in the rebuilt-list results, while still considering those items for the "other + * profile" special-treatment described in {@code rebuildList()}. + * + * @return the same (possibly null) List reference as {@code currentResolveList}, if the list is + * unmodified as a result of filtering; or, if some item(s) were removed, then either a copy of + * the original {@code currentResolveList} (if {@code returnCopyOfOriginalListIfModified} is + * true), or null (otherwise). + */ + @Nullable + List<ResolvedComponentInfo> performSecondaryResolveListFiltering( + @Nullable List<ResolvedComponentInfo> currentResolveList, + boolean returnCopyOfOriginalListIfModified) { + if ((currentResolveList == null) || currentResolveList.isEmpty()) { + return currentResolveList; } + return mResolverListController.filterLowPriority( + currentResolveList, returnCopyOfOriginalListIfModified); + } + + /** + * Update the special "other profile" UI treatment based on the components resolved for a + * newly-built list. + * + * @param otherProfileInfo the first {@code ResolvedComponentInfo} specifying a + * {@code targetUserId} other than {@code USER_CURRENT}, or null if no such component info was + * found in the process of rebuilding the list (or if any such candidates were already removed + * due to "primary filtering"). + */ + void updateOtherProfileTreatment(@Nullable ResolvedComponentInfo otherProfileInfo) { + mLastChosen = null; - if (mOtherProfile == null) { + if (otherProfileInfo != null) { + mOtherProfile = makeOtherProfileDisplayResolveInfo( + mContext, otherProfileInfo, mPm, mResolverListCommunicator, mIconDpi); + } else { + mOtherProfile = null; try { mLastChosen = mResolverListController.getLastChosen(); + // TODO: does this also somehow need to update mLastChosenPosition? If so, maybe + // the current method should also take responsibility for re-initializing + // mLastChosenPosition, where it's currently done at the start of rebuildList()? + // (Why is this related to the presence of mOtherProfile in fhe first place?) } catch (RemoteException re) { Log.d(TAG, "Error calling getLastChosenActivity\n" + re); } } + } - setPlaceholderCount(0); - int n; - if ((currentResolveList != null) && ((n = currentResolveList.size()) > 0)) { - // We only care about fixing the unfilteredList if the current resolve list and - // current resolve list are currently the same. - List<ResolvedComponentInfo> originalList = - mResolverListController.filterLowPriority(currentResolveList, - mUnfilteredResolveList == currentResolveList); - if (originalList != null) { - mUnfilteredResolveList = originalList; - } - - if (currentResolveList.size() > 1) { - int placeholderCount = currentResolveList.size(); - if (mResolverListCommunicator.useLayoutWithDefault()) { - --placeholderCount; - } - setPlaceholderCount(placeholderCount); - createSortingTask(doPostProcessing).execute(currentResolveList); - postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ false); - return false; - } else { - processSortedList(currentResolveList, doPostProcessing); - return true; - } - } else { - processSortedList(currentResolveList, doPostProcessing); + /** + * Prepare the appropriate placeholders to eventually display the final set of resolved + * components in a newly-rebuilt list, and spawn an asynchronous sorting task if necessary. + * This eventually results in a {@code onPostListReady} callback with {@code rebuildCompleted} + * true; if any asynchronous work is required, that will first be preceded by a separate + * occurrence of the callback with {@code rebuildCompleted} false (once there are placeholders + * set up to represent the pending asynchronous results). + * @return Whether we were able to do all the work to prepare the list for display + * synchronously; if false, there will eventually be two separate {@code onPostListReady} + * callbacks, first with placeholders to represent pending asynchronous results, then later when + * the results are ready for presentation. + */ + boolean finishRebuildingListWithFilteredResults( + @Nullable List<ResolvedComponentInfo> filteredResolveList, boolean doPostProcessing) { + if (filteredResolveList == null || filteredResolveList.size() < 2) { + // No asynchronous work to do. + setPlaceholderCount(0); + processSortedList(filteredResolveList, doPostProcessing); return true; } + + int placeholderCount = filteredResolveList.size(); + if (mResolverListCommunicator.useLayoutWithDefault()) { + --placeholderCount; + } + setPlaceholderCount(placeholderCount); + + // Send an "incomplete" list-ready while the async task is running. + postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ false); + createSortingTask(doPostProcessing).execute(filteredResolveList); + return false; } AsyncTask<List<ResolvedComponentInfo>, @@ -644,6 +735,59 @@ public class ResolverListAdapter extends BaseAdapter { } /** + * Find the first element in a list of {@code ResolvedComponentInfo} objects whose + * {@code ResolveInfo} specifies a {@code targetUserId} other than the current user. + * @return the first ResolvedComponentInfo targeting a non-current user, or null if there are + * none (or if the list itself is null). + */ + private static ResolvedComponentInfo getFirstNonCurrentUserResolvedComponentInfo( + @Nullable List<ResolvedComponentInfo> resolveList) { + if (resolveList == null) { + return null; + } + + for (ResolvedComponentInfo info : resolveList) { + ResolveInfo resolveInfo = info.getResolveInfoAt(0); + if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) { + return info; + } + } + return null; + } + + /** + * Set up a {@code DisplayResolveInfo} to provide "special treatment" for the first "other" + * profile in the resolve list (i.e., the first non-current profile to appear as the target user + * of an element in the resolve list). + */ + private static DisplayResolveInfo makeOtherProfileDisplayResolveInfo( + Context context, + ResolvedComponentInfo resolvedComponentInfo, + PackageManager pm, + ResolverListCommunicator resolverListCommunicator, + int iconDpi) { + ResolveInfo resolveInfo = resolvedComponentInfo.getResolveInfoAt(0); + + Intent pOrigIntent = resolverListCommunicator.getReplacementIntent( + resolveInfo.activityInfo, + resolvedComponentInfo.getIntentAt(0)); + Intent replacementIntent = resolverListCommunicator.getReplacementIntent( + resolveInfo.activityInfo, + resolverListCommunicator.getTargetIntent()); + + ResolveInfoPresentationGetter presentationGetter = + new ResolveInfoPresentationGetter(context, iconDpi, resolveInfo); + + return new DisplayResolveInfo( + resolvedComponentInfo.getIntentAt(0), + resolveInfo, + resolveInfo.loadLabel(pm), + resolveInfo.loadLabel(pm), + pOrigIntent != null ? pOrigIntent : replacementIntent, + presentationGetter); + } + + /** * Necessary methods to communicate between {@link ResolverListAdapter} * and {@link ResolverActivity}. */ diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 9a95e6411fa4..27573631b2ce 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -393,14 +393,6 @@ public class ResolverListController { return mResolverComparator.getScore(componentName); } - /** - * Returns the list of top K component names which have highest - * {@link #getScore(DisplayResolveInfo)} - */ - public List<ComponentName> getTopComponentNames(int topK) { - return mResolverComparator.getTopComponentNames(topK); - } - public void updateModel(ComponentName componentName) { mResolverComparator.updateModel(componentName); } diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java index cb946c0dcf99..c5b21ac4da90 100644 --- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java @@ -43,12 +43,12 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.text.Collator; import java.util.ArrayList; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; /** * Ranks and compares packages based on usage stats and uses the {@link ResolverRankerService}. @@ -83,6 +83,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator private ResolverRankerServiceConnection mConnection; private Context mContext; private CountDownLatch mConnectSignal; + private ResolverRankerServiceComparatorModel mComparatorModel; public ResolverRankerServiceResolverComparator(Context context, Intent intent, String referrerPackage, AfterCompute afterCompute, @@ -99,6 +100,8 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator mRankerServiceName = new ComponentName(mContext, this.getClass()); setCallBack(afterCompute); setChooserActivityLogger(chooserActivityLogger); + + mComparatorModel = buildUpdatedModel(); } @Override @@ -125,6 +128,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } if (isUpdated) { mRankerServiceName = mResolvedRankerName; + mComparatorModel = buildUpdatedModel(); } } else { Log.e(TAG, "Sizes of sent and received ResolverTargets diff."); @@ -218,83 +222,25 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } } predictSelectProbabilities(mTargets); + + mComparatorModel = buildUpdatedModel(); } @Override public int compare(ResolveInfo lhs, ResolveInfo rhs) { - if (mStats != null) { - final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName( - lhs.activityInfo.packageName, lhs.activityInfo.name)); - final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName( - rhs.activityInfo.packageName, rhs.activityInfo.name)); - - if (lhsTarget != null && rhsTarget != null) { - final int selectProbabilityDiff = Float.compare( - rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability()); - - if (selectProbabilityDiff != 0) { - return selectProbabilityDiff > 0 ? 1 : -1; - } - } - } - - CharSequence sa = lhs.loadLabel(mPm); - if (sa == null) sa = lhs.activityInfo.name; - CharSequence sb = rhs.loadLabel(mPm); - if (sb == null) sb = rhs.activityInfo.name; - - return mCollator.compare(sa.toString().trim(), sb.toString().trim()); + return mComparatorModel.getComparator().compare(lhs, rhs); } @Override public float getScore(ComponentName name) { - final ResolverTarget target = mTargetsDict.get(name); - if (target != null) { - return target.getSelectProbability(); - } - return 0; - } - - @Override - List<ComponentName> getTopComponentNames(int topK) { - return mTargetsDict.entrySet().stream() - .sorted((o1, o2) -> -Float.compare(getScore(o1.getKey()), getScore(o2.getKey()))) - .limit(topK) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); + return mComparatorModel.getScore(name); } // update ranking model when the connection to it is valid. @Override public void updateModel(ComponentName componentName) { synchronized (mLock) { - if (mRanker != null) { - try { - int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet()) - .indexOf(componentName); - if (selectedPos >= 0 && mTargets != null) { - final float selectedProbability = getScore(componentName); - int order = 0; - for (ResolverTarget target : mTargets) { - if (target.getSelectProbability() > selectedProbability) { - order++; - } - } - logMetrics(order); - mRanker.train(mTargets, selectedPos); - } else { - if (DEBUG) { - Log.d(TAG, "Selected a unknown component: " + componentName); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Error in Train: " + e); - } - } else { - if (DEBUG) { - Log.d(TAG, "Ranker is null; skip updateModel."); - } - } + mComparatorModel.notifyOnTargetSelected(componentName); } } @@ -313,19 +259,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } } - // records metrics for evaluation. - private void logMetrics(int selectedPos) { - if (mRankerServiceName != null) { - MetricsLogger metricsLogger = new MetricsLogger(); - LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED); - log.setComponentName(mRankerServiceName); - int isCategoryUsed = (mAnnotations == null) ? 0 : 1; - log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, isCategoryUsed); - log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos); - metricsLogger.write(log); - } - } - // connect to a ranking service. private void initRanker(Context context) { synchronized (mLock) { @@ -426,6 +359,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } synchronized (mLock) { mRanker = IResolverRankerService.Stub.asInterface(service); + mComparatorModel = buildUpdatedModel(); mConnectSignal.countDown(); } } @@ -443,6 +377,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator public void destroy() { synchronized (mLock) { mRanker = null; + mComparatorModel = buildUpdatedModel(); } } } @@ -453,6 +388,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator mTargetsDict.clear(); mTargets = null; mRankerServiceName = new ComponentName(mContext, this.getClass()); + mComparatorModel = buildUpdatedModel(); mResolvedRankerName = null; initRanker(mContext); } @@ -508,4 +444,155 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } return false; } + + /** + * Re-construct a {@code ResolverRankerServiceComparatorModel} to replace the current model + * instance (if any) using the up-to-date {@code ResolverRankerServiceResolverComparator} ivar + * values. + * + * TODO: each time we replace the model instance, we're either updating the model to use + * adjusted data (which is appropriate), or we're providing a (late) value for one of our ivars + * that wasn't available the last time the model was updated. For those latter cases, we should + * just avoid creating the model altogether until we have all the prerequisites we'll need. Then + * we can probably simplify the logic in {@code ResolverRankerServiceComparatorModel} since we + * won't need to handle edge cases when the model data isn't fully prepared. + * (In some cases, these kinds of "updates" might interleave -- e.g., we might have finished + * initializing the first time and now want to adjust some data, but still need to wait for + * changes to propagate to the other ivars before rebuilding the model.) + */ + private ResolverRankerServiceComparatorModel buildUpdatedModel() { + // TODO: we don't currently guarantee that the underlying target list/map won't be mutated, + // so the ResolverComparatorModel may provide inconsistent results. We should make immutable + // copies of the data (waiting for any necessary remaining data before creating the model). + return new ResolverRankerServiceComparatorModel( + mStats, + mTargetsDict, + mTargets, + mCollator, + mRanker, + mRankerServiceName, + (mAnnotations != null), + mPm); + } + + /** + * Implementation of a {@code ResolverComparatorModel} that provides the same ranking logic as + * the legacy {@code ResolverRankerServiceResolverComparator}, as a refactoring step toward + * removing the complex legacy API. + */ + static class ResolverRankerServiceComparatorModel implements ResolverComparatorModel { + private final Map<String, UsageStats> mStats; // Treat as immutable. + private final Map<ComponentName, ResolverTarget> mTargetsDict; // Treat as immutable. + private final List<ResolverTarget> mTargets; // Treat as immutable. + private final Collator mCollator; + private final IResolverRankerService mRanker; + private final ComponentName mRankerServiceName; + private final boolean mAnnotationsUsed; + private final PackageManager mPm; + + // TODO: it doesn't look like we should have to pass both targets and targetsDict, but it's + // not written in a way that makes it clear whether we can derive one from the other (at + // least in this constructor). + ResolverRankerServiceComparatorModel( + Map<String, UsageStats> stats, + Map<ComponentName, ResolverTarget> targetsDict, + List<ResolverTarget> targets, + Collator collator, + IResolverRankerService ranker, + ComponentName rankerServiceName, + boolean annotationsUsed, + PackageManager pm) { + mStats = stats; + mTargetsDict = targetsDict; + mTargets = targets; + mCollator = collator; + mRanker = ranker; + mRankerServiceName = rankerServiceName; + mAnnotationsUsed = annotationsUsed; + mPm = pm; + } + + @Override + public Comparator<ResolveInfo> getComparator() { + // TODO: doCompute() doesn't seem to be concerned about null-checking mStats. Is that + // a bug there, or do we have a way of knowing it will be non-null under certain + // conditions? + return (lhs, rhs) -> { + if (mStats != null) { + final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName( + lhs.activityInfo.packageName, lhs.activityInfo.name)); + final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName( + rhs.activityInfo.packageName, rhs.activityInfo.name)); + + if (lhsTarget != null && rhsTarget != null) { + final int selectProbabilityDiff = Float.compare( + rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability()); + + if (selectProbabilityDiff != 0) { + return selectProbabilityDiff > 0 ? 1 : -1; + } + } + } + + CharSequence sa = lhs.loadLabel(mPm); + if (sa == null) sa = lhs.activityInfo.name; + CharSequence sb = rhs.loadLabel(mPm); + if (sb == null) sb = rhs.activityInfo.name; + + return mCollator.compare(sa.toString().trim(), sb.toString().trim()); + }; + } + + @Override + public float getScore(ComponentName name) { + final ResolverTarget target = mTargetsDict.get(name); + if (target != null) { + return target.getSelectProbability(); + } + return 0; + } + + @Override + public void notifyOnTargetSelected(ComponentName componentName) { + if (mRanker != null) { + try { + int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet()) + .indexOf(componentName); + if (selectedPos >= 0 && mTargets != null) { + final float selectedProbability = getScore(componentName); + int order = 0; + for (ResolverTarget target : mTargets) { + if (target.getSelectProbability() > selectedProbability) { + order++; + } + } + logMetrics(order); + mRanker.train(mTargets, selectedPos); + } else { + if (DEBUG) { + Log.d(TAG, "Selected a unknown component: " + componentName); + } + } + } catch (RemoteException e) { + Log.e(TAG, "Error in Train: " + e); + } + } else { + if (DEBUG) { + Log.d(TAG, "Ranker is null; skip updateModel."); + } + } + } + + /** Records metrics for evaluation. */ + private void logMetrics(int selectedPos) { + if (mRankerServiceName != null) { + MetricsLogger metricsLogger = new MetricsLogger(); + LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED); + log.setComponentName(mRankerServiceName); + log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed); + log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos); + metricsLogger.write(log); + } + } + } } diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java index 43dacd72db37..354eb62ba045 100644 --- a/core/java/com/android/internal/app/SimpleIconFactory.java +++ b/core/java/com/android/internal/app/SimpleIconFactory.java @@ -71,7 +71,7 @@ public class SimpleIconFactory { new SynchronizedPool<>(Runtime.getRuntime().availableProcessors()); private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; - private static final float BLUR_FACTOR = 0.5f / 48; + private static final float BLUR_FACTOR = 1.5f / 48; private Context mContext; private Canvas mCanvas; @@ -650,8 +650,8 @@ public class SimpleIconFactory { /* Shadow generator block */ private static final float KEY_SHADOW_DISTANCE = 1f / 48; - private static final int KEY_SHADOW_ALPHA = 61; - private static final int AMBIENT_SHADOW_ALPHA = 30; + private static final int KEY_SHADOW_ALPHA = 10; + private static final int AMBIENT_SHADOW_ALPHA = 7; private Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); private Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 8847a490e39c..554155865080 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -47,6 +47,7 @@ interface IPlatformCompat { * @param appInfo representing the affected app * @throws SecurityException if logging is not allowed */ + @EnforcePermission("LOG_COMPAT_CHANGE") void reportChange(long changeId, in ApplicationInfo appInfo); /** @@ -60,6 +61,7 @@ interface IPlatformCompat { * @param packageName the package name of the app in question * @throws SecurityException if logging is not allowed */ + @EnforcePermission("LOG_COMPAT_CHANGE") void reportChangeByPackageName(long changeId, in String packageName, int userId); /** @@ -72,6 +74,7 @@ interface IPlatformCompat { * @param uid the UID of the app in question * @throws SecurityException if logging is not allowed */ + @EnforcePermission("LOG_COMPAT_CHANGE") void reportChangeByUid(long changeId, int uid); /** @@ -90,6 +93,7 @@ interface IPlatformCompat { * @return {@code true} if the change is enabled for the current app * @throws SecurityException if logging or reading compat confis is not allowed */ + @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"}) boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo); /** @@ -115,6 +119,7 @@ interface IPlatformCompat { * @return {@code true} if the change is enabled for the current app * @throws SecurityException if logging or reading compat confis is not allowed */ + @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"}) boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId); /** @@ -140,6 +145,7 @@ interface IPlatformCompat { * @return {@code true} if the change is enabled for the current app * @throws SecurityException if logging or reading compat confis is not allowed */ + @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"}) boolean isChangeEnabledByUid(long changeId, int uid); /** @@ -151,6 +157,7 @@ interface IPlatformCompat { * @param packageName the package name of the app whose changes will be overridden * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") void setOverrides(in CompatibilityChangeConfig overrides, in String packageName); /** @@ -171,6 +178,7 @@ interface IPlatformCompat { * on specific apps by their package name * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD") void putAllOverridesOnReleaseBuilds(in CompatibilityOverridesByPackageConfig overridesByPackage); /** @@ -190,6 +198,7 @@ interface IPlatformCompat { * @param packageName the package name of the app whose changes will be overridden * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD") void putOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName); /** @@ -201,6 +210,7 @@ interface IPlatformCompat { * @param packageName the package name of the app whose changes will be overridden * @throws SecurityException if overriding changes is not permitted. */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName); /** @@ -213,6 +223,7 @@ interface IPlatformCompat { * @return {@code true} if an override existed * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") boolean clearOverride(long changeId, String packageName); /** @@ -225,6 +236,7 @@ interface IPlatformCompat { * @return {@code true} if an override existed * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") boolean clearOverrideForTest(long changeId, String packageName); /** @@ -245,6 +257,7 @@ interface IPlatformCompat { * removed for specific apps by their package name * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD") void removeAllOverridesOnReleaseBuilds(in CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage); /** @@ -266,6 +279,7 @@ interface IPlatformCompat { * default behaviour * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD") void removeOverridesOnReleaseBuilds(in CompatibilityOverridesToRemoveConfig overridesToRemove, in String packageName); /** @@ -280,6 +294,7 @@ interface IPlatformCompat { * @return The number of changes that were enabled. * @throws SecurityException if overriding changes is not permitted. */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") int enableTargetSdkChanges(in String packageName, int targetSdkVersion); /** @@ -294,6 +309,7 @@ interface IPlatformCompat { * @return the number of changes that were disabled * @throws SecurityException if overriding changes is not permitted. */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") int disableTargetSdkChanges(in String packageName, int targetSdkVersion); /** @@ -304,6 +320,7 @@ interface IPlatformCompat { * @param packageName the package name of the app whose overrides will be cleared * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") void clearOverrides(in String packageName); /** @@ -314,6 +331,7 @@ interface IPlatformCompat { * @param packageName the package name of the app whose overrides will be cleared * @throws SecurityException if overriding changes is not permitted */ + @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG") void clearOverridesForTest(in String packageName); /** @@ -323,6 +341,7 @@ interface IPlatformCompat { * @return a {@link CompatibilityChangeConfig}, representing whether a change is enabled for * the given app or not */ + @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"}) CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo); /** @@ -330,6 +349,7 @@ interface IPlatformCompat { * * @return an array of {@link CompatibilityChangeInfo} known to the service */ + @EnforcePermission("READ_COMPAT_CHANGE_CONFIG") CompatibilityChangeInfo[] listAllChanges(); /** @@ -338,10 +358,12 @@ interface IPlatformCompat { * * @return an array of {@link CompatibilityChangeInfo} */ + @RequiresNoPermission CompatibilityChangeInfo[] listUIChanges(); /** * Gets an instance that can determine whether a changeid can be overridden for a package name. */ + @RequiresNoPermission IOverrideValidator getOverrideValidator(); } diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index c94438e3cee8..ffb37529dd6e 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -520,6 +520,13 @@ public final class SystemUiDeviceConfigFlags { public static final String USE_UNBUNDLED_SHARESHEET = "use_unbundled_sharesheet"; /** + * (int) The delay (in ms) before refreshing the Sharesheet UI after a change to the share + * target data model. For more info see go/sharesheet-list-view-update-delay. + */ + public static final String SHARESHEET_LIST_VIEW_UPDATE_DELAY = + "sharesheet_list_view_update_delay"; + + /** * (string) Name of the default QR code scanner activity. On the eligible devices this activity * is provided by GMS core. */ diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index a60b31078a86..73a4f1c4a154 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -62,7 +62,9 @@ import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -232,9 +234,9 @@ public abstract class FileSystemProvider extends DocumentsProvider { throw new FileNotFoundException(doc + " is not found under " + parent); } - LinkedList<String> path = new LinkedList<>(); + List<String> path = new ArrayList<>(); while (doc != null && FileUtils.contains(parent, doc)) { - path.addFirst(getDocIdForFile(doc)); + path.add(0, getDocIdForFile(doc)); doc = doc.getParentFile(); } @@ -448,10 +450,10 @@ public abstract class FileSystemProvider extends DocumentsProvider { File folder, String[] projection, Set<String> exclusion, Bundle queryArgs) throws FileNotFoundException { final MatrixCursor result = new MatrixCursor(resolveProjection(projection)); - final LinkedList<File> pending = new LinkedList<>(); + final List<File> pending = new ArrayList<>(); pending.add(folder); while (!pending.isEmpty() && result.getCount() < 24) { - final File file = pending.removeFirst(); + final File file = pending.remove(0); if (shouldHide(file)) continue; if (file.isDirectory()) { diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index b786526ce676..828e5cf1fd4c 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -183,7 +183,7 @@ public class OverlayConfig { final ParsedOverlayInfo p = partitionOverlayInfos.get(j); if (p.isStatic) { partitionConfigs.add(new ParsedConfiguration(p.packageName, - true /* enabled */, false /* mutable */, partition.policy, p)); + true /* enabled */, false /* mutable */, partition.policy, p, null)); } } diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java index 0ab7b3d2ef86..5ab77d8d3236 100644 --- a/core/java/com/android/internal/content/om/OverlayConfigParser.java +++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackagePartitions; import android.content.pm.PackagePartitions.SystemPartition; +import android.os.Build; import android.os.FileUtils; import android.util.ArraySet; import android.util.Log; @@ -56,6 +57,34 @@ import java.util.Map; **/ final class OverlayConfigParser { + /** Represents a part of a parsed overlay configuration XML file. */ + public static class ParsedConfigFile { + @NonNull public final String path; + @NonNull public final int line; + @Nullable public final String xml; + + ParsedConfigFile(@NonNull String path, int line, @Nullable String xml) { + this.path = path; + this.line = line; + this.xml = xml; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append("{path="); + sb.append(path); + sb.append(", line="); + sb.append(line); + if (xml != null) { + sb.append(", xml="); + sb.append(xml); + } + sb.append("}"); + return sb.toString(); + } + } + // Default values for overlay configurations. static final boolean DEFAULT_ENABLED_STATE = false; static final boolean DEFAULT_MUTABILITY = true; @@ -94,24 +123,40 @@ final class OverlayConfigParser { @NonNull public final String policy; - /** Information extracted from the manifest of the overlay. */ - @NonNull + /** + * Information extracted from the manifest of the overlay. + * Null if the information was read from a config file instead of a manifest. + * + * @see parsedConfigFile + **/ + @Nullable public final ParsedOverlayInfo parsedInfo; + /** + * The config file used to configure this overlay. + * Null if no config file was used, in which case the overlay's manifest was used instead. + * + * @see parsedInfo + **/ + @Nullable + public final ParsedConfigFile parsedConfigFile; + ParsedConfiguration(@NonNull String packageName, boolean enabled, boolean mutable, - @NonNull String policy, @NonNull ParsedOverlayInfo parsedInfo) { + @NonNull String policy, @Nullable ParsedOverlayInfo parsedInfo, + @Nullable ParsedConfigFile parsedConfigFile) { this.packageName = packageName; this.enabled = enabled; this.mutable = mutable; this.policy = policy; this.parsedInfo = parsedInfo; + this.parsedConfigFile = parsedConfigFile; } @Override public String toString() { return getClass().getSimpleName() + String.format("{packageName=%s, enabled=%s" - + ", mutable=%s, policy=%s, parsedInfo=%s}", packageName, enabled, - mutable, policy, parsedInfo); + + ", mutable=%s, policy=%s, parsedInfo=%s, parsedConfigFile=%s}", + packageName, enabled, mutable, policy, parsedInfo, parsedConfigFile); } } @@ -417,9 +462,26 @@ final class OverlayConfigParser { Log.w(TAG, "found default-disabled immutable overlay " + packageName); } - final ParsedConfiguration Config = new ParsedConfiguration(packageName, isEnabled, - isMutable, parsingContext.mPartition.policy, info); + final ParsedConfigFile parsedConfigFile = new ParsedConfigFile( + configFile.getPath().intern(), parser.getLineNumber(), + (Build.IS_ENG || Build.IS_USERDEBUG) ? currentParserContextToString(parser) : null); + final ParsedConfiguration config = new ParsedConfiguration(packageName, isEnabled, + isMutable, parsingContext.mPartition.policy, info, parsedConfigFile); parsingContext.mConfiguredOverlays.add(packageName); - parsingContext.mOrderedConfigurations.add(Config); + parsingContext.mOrderedConfigurations.add(config); + } + + private static String currentParserContextToString(@NonNull XmlPullParser parser) { + StringBuilder sb = new StringBuilder("<"); + sb.append(parser.getName()); + sb.append(" "); + for (int i = 0; i < parser.getAttributeCount(); i++) { + sb.append(parser.getAttributeName(i)); + sb.append("=\""); + sb.append(parser.getAttributeValue(i)); + sb.append("\" "); + } + sb.append("/>"); + return sb.toString(); } } diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index 2ee47b64b1a5..4babb7080176 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -34,7 +34,7 @@ oneway interface IInputMethodPrivilegedOperations { void setInputMethod(String id, in AndroidFuture future /* T=Void */); void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype, in AndroidFuture future /* T=Void */); - void hideMySoftInput(int flags, in AndroidFuture future /* T=Void */); + void hideMySoftInput(int flags, int reason, in AndroidFuture future /* T=Void */); void showMySoftInput(int flags, in AndroidFuture future /* T=Void */); void updateStatusIconAsync(String packageName, int iconId); void switchToPreviousInputMethod(in AndroidFuture future /* T=Boolean */); diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index d6697684f79e..97ad0840fbd5 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -194,12 +194,12 @@ public final class InputMethodDebug { return "SHOW_SOFT_INPUT"; case SoftInputShowHideReason.ATTACH_NEW_INPUT: return "ATTACH_NEW_INPUT"; - case SoftInputShowHideReason.SHOW_MY_SOFT_INPUT: - return "SHOW_MY_SOFT_INPUT"; + case SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME: + return "SHOW_SOFT_INPUT_FROM_IME"; case SoftInputShowHideReason.HIDE_SOFT_INPUT: return "HIDE_SOFT_INPUT"; - case SoftInputShowHideReason.HIDE_MY_SOFT_INPUT: - return "HIDE_MY_SOFT_INPUT"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME: + return "HIDE_SOFT_INPUT_FROM_IME"; case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV: return "SHOW_AUTO_EDITOR_FORWARD_NAV"; case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV: @@ -242,6 +242,16 @@ public final class InputMethodDebug { return "SHOW_SOFT_INPUT_BY_INSETS_API"; case SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE: return "HIDE_DISPLAY_IME_POLICY_HIDE"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API: + return "HIDE_SOFT_INPUT_BY_INSETS_API"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY: + return "HIDE_SOFT_INPUT_BY_BACK_KEY"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT: + return "HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED: + return "HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION: + return "HIDE_SOFT_INPUT_IMM_DEPRECATION"; default: return "Unknown=" + reason; } diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index 15d7acfb6e0a..67c2103450bb 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -253,18 +253,19 @@ public final class InputMethodPrivilegedOperations { * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, IVoidResultCallback)} * * @param flags additional operating flags + * @param reason the reason to hide soft input * @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY * @see android.view.inputmethod.InputMethodManager#HIDE_NOT_ALWAYS */ @AnyThread - public void hideMySoftInput(int flags) { + public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; } try { final AndroidFuture<Void> future = new AndroidFuture<>(); - ops.hideMySoftInput(flags, future); + ops.hideMySoftInput(flags, reason, future); CompletableFutureUtil.getResult(future); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java index 9e5776292031..97ad5cb4663c 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -19,8 +19,11 @@ package com.android.internal.inputmethod; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.os.IBinder; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import java.lang.annotation.Retention; @@ -31,9 +34,9 @@ import java.lang.annotation.Retention; @IntDef(value = { SoftInputShowHideReason.SHOW_SOFT_INPUT, SoftInputShowHideReason.ATTACH_NEW_INPUT, - SoftInputShowHideReason.SHOW_MY_SOFT_INPUT, + SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME, SoftInputShowHideReason.HIDE_SOFT_INPUT, - SoftInputShowHideReason.HIDE_MY_SOFT_INPUT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME, SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV, SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV, SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE, @@ -55,7 +58,12 @@ import java.lang.annotation.Retention; SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT, SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT, SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API, - SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE}) + SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, + SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, + SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY, + SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED, + SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION}) public @interface SoftInputShowHideReason { /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ int SHOW_SOFT_INPUT = 0; @@ -63,8 +71,12 @@ public @interface SoftInputShowHideReason { /** Show soft input when {@code InputMethodManagerService#attachNewInputLocked} called. */ int ATTACH_NEW_INPUT = 1; - /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. */ - int SHOW_MY_SOFT_INPUT = 2; + /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. This is triggered when + * the IME process try to show the keyboard. + * + * @see android.inputmethodservice.InputMethodService#requestShowSelf(int) + */ + int SHOW_SOFT_INPUT_FROM_IME = 2; /** * Hide soft input by @@ -72,8 +84,11 @@ public @interface SoftInputShowHideReason { */ int HIDE_SOFT_INPUT = 3; - /** Hide soft input by {@code InputMethodManagerService#hideMySoftInput}. */ - int HIDE_MY_SOFT_INPUT = 4; + /** + * Hide soft input by + * {@link android.inputmethodservice.InputMethodService#requestHideSelf(int)}. + */ + int HIDE_SOFT_INPUT_FROM_IME = 4; /** * Show soft input when navigated forward to the window (with @@ -203,4 +218,32 @@ public @interface SoftInputShowHideReason { * See also {@code InputMethodManagerService#mImeHiddenByDisplayPolicy}. */ int HIDE_DISPLAY_IME_POLICY_HIDE = 26; + + /** + * Hide soft input by {@link android.view.InsetsController#hide(int)}. + */ + int HIDE_SOFT_INPUT_BY_INSETS_API = 27; + + /** + * Hide soft input by {@link android.inputmethodservice.InputMethodService#handleBack(boolean)}. + */ + int HIDE_SOFT_INPUT_BY_BACK_KEY = 28; + + /** + * Hide soft input by + * {@link android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int)}. + */ + int HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT = 29; + + /** + * Hide soft input by + * {@link android.inputmethodservice.InputMethodService#onExtractingInputChanged(EditorInfo)})}. + */ + int HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED = 30; + + /** + * Hide soft input by the deprecated + * {@link InputMethodManager#hideSoftInputFromInputMethod(IBinder, int)}. + */ + int HIDE_SOFT_INPUT_IMM_DEPRECATION = 31; } diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java index bf3e8d56b6bc..a0f7905771a2 100644 --- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java +++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java @@ -1,11 +1,5 @@ package com.android.internal.view.menu; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - import android.annotation.AttrRes; import android.annotation.IntDef; import android.annotation.NonNull; @@ -22,16 +16,16 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewTreeObserver; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnKeyListener; +import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.AbsListView; import android.widget.FrameLayout; import android.widget.HeaderViewListAdapter; import android.widget.ListAdapter; -import android.widget.MenuItemHoverListener; import android.widget.ListView; +import android.widget.MenuItemHoverListener; import android.widget.MenuPopupWindow; import android.widget.PopupWindow; import android.widget.PopupWindow.OnDismissListener; @@ -40,6 +34,11 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + /** * A popup for a menu which will allow multiple submenus to appear in a cascading fashion, side by * side. @@ -70,7 +69,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey private final Handler mSubMenuHoverHandler; /** List of menus that were added before this popup was shown. */ - private final List<MenuBuilder> mPendingMenus = new LinkedList<>(); + private final List<MenuBuilder> mPendingMenus = new ArrayList<>(); /** * List of open menus. The first item is the root menu and each diff --git a/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java b/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java index 481183e700a2..c1e2840f0a8b 100644 --- a/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java +++ b/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java @@ -23,6 +23,7 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.RemoteViews; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -96,7 +97,7 @@ public class ConversationHeaderLinearLayout extends LinearLayout { continue; } if (visibleChildrenToShorten == null) { - visibleChildrenToShorten = new LinkedList<>(); + visibleChildrenToShorten = new ArrayList<>(count); } visibleChildrenToShorten.add(new ViewInfo(child)); remainingWeight += Math.max(0, weight); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index a94b30707604..6771cdf84717 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -39,7 +39,6 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Build; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -47,7 +46,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.text.TextUtils; diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java index 8c61a12b47e6..95a4e1238e06 100644 --- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java +++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java @@ -61,10 +61,10 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -950,9 +950,9 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup { int availableWidth = toolbarWidth; - final LinkedList<MenuItem> remainingMenuItems = new LinkedList<>(); + final ArrayList<MenuItem> remainingMenuItems = new ArrayList<>(); // add the overflow menu items to the end of the remainingMenuItems list. - final LinkedList<MenuItem> overflowMenuItems = new LinkedList(); + final ArrayList<MenuItem> overflowMenuItems = new ArrayList<>(); for (MenuItem menuItem : menuItems) { if (menuItem.getItemId() != android.R.id.textAssist && menuItem.requiresOverflow()) { @@ -969,7 +969,7 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup { int lastGroupId = -1; boolean isFirstItem = true; while (!remainingMenuItems.isEmpty()) { - final MenuItem menuItem = remainingMenuItems.peek(); + final MenuItem menuItem = remainingMenuItems.get(0); // if this is the first item, regardless of requiresOverflow(), it should be // displayed on the main panel. Otherwise all items including this one will be @@ -1022,7 +1022,7 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup { params.width = menuItemButtonWidth; menuItemButton.setLayoutParams(params); availableWidth -= menuItemButtonWidth; - remainingMenuItems.pop(); + remainingMenuItems.remove(0); } else { break; } diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index eba6cca76389..817b3154889b 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -676,7 +676,6 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p char dex2oatFlagsBuf[PROPERTY_VALUE_MAX]; char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX]; char extraOptsBuf[PROPERTY_VALUE_MAX]; - char voldDecryptBuf[PROPERTY_VALUE_MAX]; char perfettoHprofOptBuf[sizeof("-XX:PerfettoHprof=") + PROPERTY_VALUE_MAX]; char perfettoJavaHeapStackOptBuf[ sizeof("-XX:PerfettoJavaHeapStackProf=") + PROPERTY_VALUE_MAX]; @@ -958,19 +957,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p addOption("-Xint:jit"); } - // If we are booting without the real /data, don't spend time compiling. - property_get("vold.decrypt", voldDecryptBuf, ""); - bool skip_compilation = ((strcmp(voldDecryptBuf, "trigger_restart_min_framework") == 0) || - (strcmp(voldDecryptBuf, "1") == 0)); - // Extra options for JIT. - if (skip_compilation) { - addOption("-Xcompiler-option"); - addOption("--compiler-filter=assume-verified"); - } else { - parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf, - "--compiler-filter=", "-Xcompiler-option"); - } + parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf, + "--compiler-filter=", "-Xcompiler-option"); parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option"); parseCompilerOption("dalvik.vm.dex2oat-cpu-set", dex2oatCpuSetBuf, "--cpu-set=", "-Xcompiler-option"); @@ -1011,52 +1000,48 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p parseExtraOpts(extraOptsBuf, NULL); // Extra options for boot image generation. - if (skip_compilation) { - addOption("-Xnoimage-dex2oat"); - } else { - parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf, - "-Xms", "-Ximage-compiler-option"); - parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf, - "-Xmx", "-Ximage-compiler-option"); - - parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf, - "--compiler-filter=", "-Ximage-compiler-option"); - - // If there is a dirty-image-objects file, push it. - if (hasFile("/system/etc/dirty-image-objects")) { - addOption("-Ximage-compiler-option"); - addOption("--dirty-image-objects=/system/etc/dirty-image-objects"); - } + parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf, + "-Xms", "-Ximage-compiler-option"); + parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf, + "-Xmx", "-Ximage-compiler-option"); + + parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf, + "--compiler-filter=", "-Ximage-compiler-option"); + + // If there is a dirty-image-objects file, push it. + if (hasFile("/system/etc/dirty-image-objects")) { + addOption("-Ximage-compiler-option"); + addOption("--dirty-image-objects=/system/etc/dirty-image-objects"); + } - parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j", - "-Ximage-compiler-option"); - parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=", - "-Ximage-compiler-option"); - - // The runtime may compile a boot image, when necessary, not using installd. Thus, we need - // to pass the instruction-set-features/variant as an image-compiler-option. - // Note: it is OK to reuse the buffer, as the values are exactly the same between - // * compiler-option, used for runtime compilation (DexClassLoader) - // * image-compiler-option, used for boot-image compilation on device - parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant, - "--instruction-set-variant=", "-Ximage-compiler-option"); - parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features, - "--instruction-set-features=", "-Ximage-compiler-option"); - - if (generate_debug_info) { - addOption("-Ximage-compiler-option"); - addOption("--generate-debug-info"); - } + parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j", + "-Ximage-compiler-option"); + parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=", + "-Ximage-compiler-option"); - if (generate_mini_debug_info) { - addOption("-Ximage-compiler-option"); - addOption("--generate-mini-debug-info"); - } + // The runtime may compile a boot image, when necessary, not using installd. Thus, we need + // to pass the instruction-set-features/variant as an image-compiler-option. + // Note: it is OK to reuse the buffer, as the values are exactly the same between + // * compiler-option, used for runtime compilation (DexClassLoader) + // * image-compiler-option, used for boot-image compilation on device + parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant, + "--instruction-set-variant=", "-Ximage-compiler-option"); + parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features, + "--instruction-set-features=", "-Ximage-compiler-option"); - property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, ""); - parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option"); + if (generate_debug_info) { + addOption("-Ximage-compiler-option"); + addOption("--generate-debug-info"); } + if (generate_mini_debug_info) { + addOption("-Ximage-compiler-option"); + addOption("--generate-mini-debug-info"); + } + + property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, ""); + parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option"); + /* Set the properties for locale */ { strcpy(localeOption, "-Duser.locale="); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 51a708b76801..77a26030292d 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1957,8 +1957,10 @@ static void nativeSetFrameTimelineVsync(JNIEnv* env, jclass clazz, jlong transac jlong frameTimelineVsyncId) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - transaction->setFrameTimelineInfo( - {frameTimelineVsyncId, android::os::IInputConstants::INVALID_INPUT_EVENT_ID}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = frameTimelineVsyncId; + ftInfo.inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID; + transaction->setFrameTimelineInfo(ftInfo); } static void nativeAddTransactionCommittedListener(JNIEnv* env, jclass clazz, jlong transactionObj, @@ -2043,7 +2045,7 @@ static jlong nativeCreateJankDataListenerWrapper(JNIEnv* env, jclass clazz, } static jint nativeGetGPUContextPriority(JNIEnv* env, jclass clazz) { - return static_cast<jint>(SurfaceComposerClient::getGPUContextPriority()); + return static_cast<jint>(SurfaceComposerClient::getGpuContextPriority()); } static void nativeSetTransformHint(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl, diff --git a/core/proto/OWNERS b/core/proto/OWNERS index a4463e4e96c5..907093343a3f 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -16,6 +16,7 @@ roosa@google.com per-file package_item_info.proto = toddke@google.com,patb@google.com per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS +per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com,ewol@google.com # Biometrics jaggies@google.com diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4075c5f4d8ae..fc7b634068f2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2278,7 +2278,8 @@ android:protectionLevel="signature|role" /> <!-- Allows bluetooth stack to access files - @hide This should only be used by Bluetooth apk. + This should only be granted to the Bluetooth apk. + @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES) --> <permission android:name="android.permission.BLUETOOTH_STACK" android:protectionLevel="signature|role" /> @@ -3735,12 +3736,6 @@ <permission android:name="android.permission.BIND_ATTESTATION_VERIFICATION_SERVICE" android:protectionLevel="signature" /> - <!-- Allows the caller to generate keymint keys with the INCLUDE_UNIQUE_ID tag, which - uniquely identifies the device via the attestation certificate. - @hide @TestApi --> - <permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION" - android:protectionLevel="signature" /> - <!-- ========================================= --> <!-- Permissions for special development tools --> <!-- ========================================= --> @@ -4407,13 +4402,13 @@ <permission android:name="android.permission.TUNER_RESOURCE_ACCESS" android:protectionLevel="signature|privileged|vendorPrivileged" /> - <!-- This permission is required by Media Resource Manager Service when - accessing its overridePid Api. - <p>Protection level: signature + <!-- @SystemApi This permission is required by Media Resource Manager Service when + system services create MediaCodecs on behalf of other processes and apps. + <p>Protection level: signature|privileged|vendorPrivileged <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" - android:protectionLevel="signature" /> + android:protectionLevel="signature|privileged|vendorPrivileged" /> <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" /> <!-- This permission is required by Media Resource Observer Service when diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0e3840a39e25..87f3e12cc0ff 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3736,6 +3736,9 @@ "Guest" and "Reset guest". --> <bool name="config_guestUserAutoCreated">false</bool> + <!-- If true, owner can change guest user ephemeral state via UI option --> + <bool name="config_guestUserAllowEphemeralStateChange">true</bool> + <!-- Enforce strong auth on boot. Setting this to false represents a security risk and should not be ordinarily done. The only case in which this might be permissible is in a car head unit where there are hardware mechanisms to protect the device (physical keys) and not diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a9b95da8982a..e07326ba10d2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -394,6 +394,7 @@ <java-symbol type="bool" name="config_supportsInsecureLockScreen" /> <java-symbol type="bool" name="config_guestUserEphemeral" /> <java-symbol type="bool" name="config_guestUserAutoCreated" /> + <java-symbol type="bool" name="config_guestUserAllowEphemeralStateChange" /> <java-symbol type="bool" name="config_localDisplaysMirrorContent" /> <java-symbol type="array" name="config_localPrivateDisplayPorts" /> <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" /> diff --git a/core/res/res/xml-watch/default_zen_mode_config.xml b/core/res/res/xml-watch/default_zen_mode_config.xml deleted file mode 100644 index 938cc0c3f7c0..000000000000 --- a/core/res/res/xml-watch/default_zen_mode_config.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright 2016, 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. ---> - -<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. --> -<zen version="2"> - <!-- Allow starred contacts to go through only. - Repeated calls, calls, messages, reminders, events off. --> - <allow from="2" repeatCallers="false" calls="false" messages="false" reminders="false" - events="false"/> -</zen> diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index e083b0d460a2..3733bfa586d1 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -48,6 +48,7 @@ public class UriTest extends TestCase { public void testParcelling() { parcelAndUnparcel(Uri.parse("foo:bob%20lee")); parcelAndUnparcel(Uri.fromParts("foo", "bob lee", "fragment")); + parcelAndUnparcel(Uri.fromParts("https", "www.google.com", null)); parcelAndUnparcel(new Uri.Builder() .scheme("http") .authority("crazybob.org") @@ -890,9 +891,62 @@ public class UriTest extends TestCase { Throwable targetException = expected.getTargetException(); // Check that the exception was thrown for the correct reason. assertEquals("Unknown representation: 0", targetException.getMessage()); + } finally { + parcel.recycle(); } } + private Uri buildUriFromRawParcel(boolean argumentsEncoded, + String scheme, + String authority, + String path, + String query, + String fragment) { + // Representation value (from AbstractPart.REPRESENTATION_{ENCODED,DECODED}). + final int representation = argumentsEncoded ? 1 : 2; + Parcel parcel = Parcel.obtain(); + try { + parcel.writeInt(3); // hierarchical + parcel.writeString8(scheme); + parcel.writeInt(representation); + parcel.writeString8(authority); + parcel.writeInt(representation); + parcel.writeString8(path); + parcel.writeInt(representation); + parcel.writeString8(query); + parcel.writeInt(representation); + parcel.writeString8(fragment); + parcel.setDataPosition(0); + return Uri.CREATOR.createFromParcel(parcel); + } finally { + parcel.recycle(); + } + } + + public void testUnparcelMalformedPath() { + // Regression tests for b/171966843. + + // Test cases with arguments encoded (covering testing `scheme` * `authority` options). + Uri uri0 = buildUriFromRawParcel(true, "https", "google.com", "@evil.com", null, null); + assertEquals("https://google.com/@evil.com", uri0.toString()); + Uri uri1 = buildUriFromRawParcel(true, null, "google.com", "@evil.com", "name=spark", "x"); + assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString()); + Uri uri2 = buildUriFromRawParcel(true, "http:", null, "@evil.com", null, null); + assertEquals("http::/@evil.com", uri2.toString()); + Uri uri3 = buildUriFromRawParcel(true, null, null, "@evil.com", null, null); + assertEquals("@evil.com", uri3.toString()); + + // Test cases with arguments not encoded (covering testing `scheme` * `authority` options). + Uri uriA = buildUriFromRawParcel(false, "https", "google.com", "@evil.com", null, null); + assertEquals("https://google.com/%40evil.com", uriA.toString()); + Uri uriB = buildUriFromRawParcel(false, null, "google.com", "@evil.com", null, null); + assertEquals("//google.com/%40evil.com", uriB.toString()); + Uri uriC = buildUriFromRawParcel(false, "http:", null, "@evil.com", null, null); + assertEquals("http::/%40evil.com", uriC.toString()); + Uri uriD = buildUriFromRawParcel(false, null, null, "@evil.com", "name=spark", "y"); + assertEquals("%40evil.com?name%3Dspark#y", uriD.toString()); + } + public void testToSafeString() { checkToSafeString("tel:xxxxxx", "tel:Google"); checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890"); diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java index 0ebf03fab966..925da4968517 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; +import android.graphics.text.LineBreakConfig; import android.os.LocaleList; import android.platform.test.annotations.Presubmit; import android.text.Layout.Alignment; @@ -925,4 +926,24 @@ public class StaticLayoutTest { assertEquals(0, layout.getHeight(true)); assertEquals(2, layout.getLineCount()); } + + @Test + public void testBuilder_autoPhraseBreaking() { + { + // setAutoPhraseBreaking true + LineBreakConfig lineBreakConfig = new LineBreakConfig.Builder() + .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_STYLE_NONE) + .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE) + .setAutoPhraseBreaking(true) + .build(); + final String text = "これが正解。"; + // Obtain. + StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, + text.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH); + builder.setLineBreakConfig(lineBreakConfig); + builder.build(); + assertEquals(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE, + builder.getLineBreakWordStyle()); + } + } } diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java index e303934c4aad..b3b19ce7a285 100644 --- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java +++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java @@ -34,6 +34,8 @@ import android.app.Instrumentation; import android.content.Context; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.HandwritingInitiator; import android.view.InputDevice; import android.view.MotionEvent; @@ -61,23 +63,32 @@ import org.junit.runner.RunWith; public class HandwritingInitiatorTest { private static final int TOUCH_SLOP = 8; private static final long TIMEOUT = ViewConfiguration.getLongPressTimeout(); + private static final int HANDWRITING_AREA_PADDING_DIP = 20; + private static final Rect sHwArea = new Rect(100, 200, 500, 500); private HandwritingInitiator mHandwritingInitiator; private View mTestView; - private Context mContext; + private Context mContext; + private int mHandwritingAreaPaddingPx; @Before public void setup() { final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); mContext = mInstrumentation.getTargetContext(); - ViewConfiguration viewConfiguration = mock(ViewConfiguration.class); + final ViewConfiguration viewConfiguration = mock(ViewConfiguration.class); when(viewConfiguration.getScaledTouchSlop()).thenReturn(TOUCH_SLOP); + + final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class); - mHandwritingInitiator = - spy(new HandwritingInitiator(viewConfiguration, inputMethodManager)); + mHandwritingInitiator = spy(new HandwritingInitiator(viewConfiguration, inputMethodManager, + displayMetrics)); + mHandwritingAreaPaddingPx = Math.round(TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + HANDWRITING_AREA_PADDING_DIP, + displayMetrics)); mTestView = createView(sHwArea, true); mHandwritingInitiator.updateHandwritingAreasForView(mTestView); } @@ -127,6 +138,24 @@ public class HandwritingInitiatorTest { } @Test + public void onTouchEvent_startHandwriting_when_stylusMove_withinExtendedHWArea() { + mHandwritingInitiator.onInputConnectionCreated(mTestView); + final int x1 = sHwArea.left - mHandwritingAreaPaddingPx / 2; + final int y1 = sHwArea.top - mHandwritingAreaPaddingPx / 2; + MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent1); + + final int x2 = x1 + TOUCH_SLOP * 2; + final int y2 = y1; + + MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent2); + + // Stylus movement within extended HandwritingArea should trigger IMM.startHandwriting once. + verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView); + } + + @Test public void onTouchEvent_startHandwriting_inputConnectionBuiltAfterStylusMove() { final int x1 = (sHwArea.left + sHwArea.right) / 2; final int y1 = (sHwArea.top + sHwArea.bottom) / 2; @@ -145,6 +174,24 @@ public class HandwritingInitiatorTest { } @Test + public void onTouchEvent_startHandwriting_inputConnectionBuilt_stylusMoveInExtendedHWArea() { + final int x1 = sHwArea.right + mHandwritingAreaPaddingPx / 2; + final int y1 = sHwArea.bottom + mHandwritingAreaPaddingPx / 2; + MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent1); + + final int x2 = x1 + TOUCH_SLOP * 2; + final int y2 = y1; + MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent2); + + // InputConnection is created after stylus movement. + mHandwritingInitiator.onInputConnectionCreated(mTestView); + + verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView); + } + + @Test public void onTouchEvent_notStartHandwriting_when_stylusTap_withinHWArea() { mHandwritingInitiator.onInputConnectionCreated(mTestView); final int x1 = 200; @@ -213,6 +260,23 @@ public class HandwritingInitiatorTest { } @Test + public void onTouchEvent_focusView_stylusMoveOnce_withinExtendedHWArea() { + final int x1 = sHwArea.left - mHandwritingAreaPaddingPx / 2; + final int y1 = sHwArea.top - mHandwritingAreaPaddingPx / 2; + MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent1); + + final int x2 = x1 + TOUCH_SLOP * 2; + final int y2 = y1; + + MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent2); + + // HandwritingInitiator will request focus for the registered view. + verify(mTestView, times(1)).requestFocus(); + } + + @Test public void autoHandwriting_whenDisabled_wontStartHW() { View mockView = createView(sHwArea, false); mHandwritingInitiator.onInputConnectionCreated(mockView); diff --git a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java index 04b888623732..3e640c1bad39 100644 --- a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java +++ b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java @@ -115,11 +115,6 @@ public class AbstractResolverComparatorTest { @Override void handleResultMessage(Message message) {} - - @Override - List<ComponentName> getTopComponentNames(int topK) { - return null; - } }; return testComparator; } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index cf78646a466f..b38e1c274b45 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -81,7 +81,6 @@ import android.net.Uri; import android.os.UserHandle; import android.provider.DeviceConfig; import android.service.chooser.ChooserTarget; -import android.util.Log; import android.view.View; import androidx.annotation.CallSuper; @@ -623,7 +622,7 @@ public class ChooserActivityTest { List<ResolvedComponentInfo> stableCopy = createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10); waitForIdle(); - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))) .perform(click()); @@ -1437,7 +1436,7 @@ public class ChooserActivityTest { // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured // TODO: restructure the tests b/129870719 - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); assertThat("Chooser should have 3 targets (2 apps, 1 direct)", activity.getAdapter().getCount(), is(3)); @@ -1513,7 +1512,7 @@ public class ChooserActivityTest { // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured // TODO: restructure the tests b/129870719 - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); assertThat("Chooser should have 3 targets (2 apps, 1 direct)", activity.getAdapter().getCount(), is(3)); @@ -1595,7 +1594,7 @@ public class ChooserActivityTest { // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured // TODO: restructure the tests b/129870719 - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); assertThat("Chooser should have 3 targets (2 apps, 1 direct)", wrapper.getAdapter().getCount(), is(3)); @@ -1667,7 +1666,7 @@ public class ChooserActivityTest { // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured // TODO: restructure the tests b/129870719 - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); assertThat("Chooser should have 4 targets (2 apps, 2 direct)", wrapper.getAdapter().getCount(), is(4)); @@ -1754,7 +1753,7 @@ public class ChooserActivityTest { // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured // TODO: restructure the tests b/129870719 - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); assertThat( String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)", @@ -1879,12 +1878,13 @@ public class ChooserActivityTest { return true; }; - mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); + final IChooserWrapper activity = (IChooserWrapper) + mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); waitForIdle(); onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click()); waitForIdle(); // wait for the share sheet to expand - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); onView(first(allOf( withText(workResolvedComponentInfos.get(0) @@ -2023,7 +2023,7 @@ public class ChooserActivityTest { .check(matches(isDisplayed())); } - @Test + @Test @Ignore("b/222124533") public void testAppTargetLogging() throws InterruptedException { Intent sendIntent = createSendTextIntent(); List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -2042,6 +2042,10 @@ public class ChooserActivityTest { mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); + // TODO(b/222124533): other test cases use a timeout to make sure that the UI is fully + // populated; without one, this test flakes. Ideally we should address the need for a + // timeout everywhere instead of introducing one to fix this particular test. + assertThat(activity.getAdapter().getCount(), is(2)); onView(withIdFromRuntimeResource("profile_button")).check(doesNotExist()); @@ -2143,7 +2147,7 @@ public class ChooserActivityTest { // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured // TODO: restructure the tests b/129870719 - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs); assertThat("Chooser should have 3 targets (2 apps, 1 direct)", activity.getAdapter().getCount(), is(3)); @@ -2324,7 +2328,7 @@ public class ChooserActivityTest { assertThat(logger.numCalls(), is(6)); } - @Test + @Test @Ignore("b/222124533") public void testSwitchProfileLogging() throws InterruptedException { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; @@ -3069,8 +3073,15 @@ public class ChooserActivityTest { // framework code on the device is up-to-date. // TODO: is there a better way to do this? (Other than abandoning inheritance-based DI wrapper?) private int getRuntimeResourceId(String name, String defType) { - int id = mActivityRule.getActivity().getResources().getIdentifier(name, defType, "android"); + int id = -1; + if (ChooserActivityOverrideData.getInstance().resources != null) { + id = ChooserActivityOverrideData.getInstance().resources.getIdentifier( + name, defType, "android"); + } else { + id = mActivityRule.getActivity().getResources().getIdentifier(name, defType, "android"); + } assertThat(id, greaterThan(0)); + return id; } } diff --git a/core/tests/coretests/src/com/android/internal/app/FakeResolverComparatorModel.java b/core/tests/coretests/src/com/android/internal/app/FakeResolverComparatorModel.java new file mode 100644 index 000000000000..fbbe57c8e325 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/FakeResolverComparatorModel.java @@ -0,0 +1,61 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.content.ComponentName; +import android.content.pm.ResolveInfo; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * Basic {@link ResolverComparatorModel} implementation that sorts according to a pre-defined (or + * default) {@link java.util.Comparator}. + */ +public class FakeResolverComparatorModel implements ResolverComparatorModel { + private final Comparator<ResolveInfo> mComparator; + + public static FakeResolverComparatorModel makeModelFromComparator( + Comparator<ResolveInfo> comparator) { + return new FakeResolverComparatorModel(comparator); + } + + public static FakeResolverComparatorModel makeDefaultModel() { + return makeModelFromComparator(Comparator.comparing(ri -> ri.activityInfo.name)); + } + + @Override + public Comparator<ResolveInfo> getComparator() { + return mComparator; + } + + @Override + public float getScore(ComponentName name) { + return 0.0f; // Models are not required to provide numerical scores. + } + + @Override + public void notifyOnTargetSelected(ComponentName componentName) { + System.out.println( + "User selected " + componentName + " under model " + System.identityHashCode(this)); + } + + private FakeResolverComparatorModel(Comparator<ResolveInfo> comparator) { + mComparator = comparator; + } +}
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index e7a23f262753..43fba529182e 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -516,8 +516,6 @@ public class ResolverActivityTest { onView(withText(R.string.resolver_work_tab)) .perform(click()); waitForIdle(); - // wait for the share sheet to expand - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); onView(first(allOf(withText(workResolvedComponentInfos.get(0) .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed()))) .perform(click()); @@ -616,8 +614,6 @@ public class ResolverActivityTest { onView(withText(R.string.resolver_work_tab)) .perform(click()); waitForIdle(); - // wait for the share sheet to expand - Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); onView(first(allOf( withText(workResolvedComponentInfos.get(0) .getResolveInfoAt(0).activityInfo.applicationInfo.name), diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index c81473ddcfc6..c117816352c7 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -430,8 +430,8 @@ axis 0x05 RZ axis 0x06 THROTTLE axis 0x07 RUDDER axis 0x08 WHEEL -axis 0x09 GAS -axis 0x0a BRAKE +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER axis 0x10 HAT_X axis 0x11 HAT_Y diff --git a/errorprone/Android.bp b/errorprone/Android.bp index a927f53e3b5f..8f32f0eab37f 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -1,4 +1,3 @@ - package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import @@ -42,8 +41,10 @@ java_test_host { static_libs: [ "truth-prebuilt", "kxml2-2.3.0", + "compile-testing-prebuilt", "error_prone_android_framework_lib", "error_prone_test_helpers", + "google_java_format", "hamcrest-library", "hamcrest", "platform-test-annotations", diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java new file mode 100644 index 000000000000..07f1d4a09006 --- /dev/null +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2022 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.google.errorprone.bugpatterns.android; + +import static com.google.errorprone.BugPattern.LinkType.NONE; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.matchers.Description.NO_MATCH; +import static com.google.errorprone.util.ASTHelpers.getStartPosition; +import static com.google.errorprone.util.ASTHelpers.getSymbol; + +import com.google.auto.service.AutoService; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.fixes.SuggestedFix; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.util.ASTHelpers; +import com.google.errorprone.util.ErrorProneToken; +import com.google.errorprone.util.ErrorProneTokens; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import com.sun.tools.javac.parser.Tokens; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import javax.lang.model.element.ElementKind; + +/** + * Bug checker to warn about {@code @hide} directives in comments. + * + * {@code @hide} tags are only meaningful inside of Javadoc comments. Errorprone has checks for + * standard Javadoc tags but doesn't know anything about {@code @hide} since it's an Android + * specific tag. + */ +@AutoService(BugChecker.class) +@BugPattern( + name = "AndroidHideInComments", + summary = "Warns when there are @hide declarations in comments rather than javadoc", + linkType = NONE, + severity = WARNING) +public class HideInCommentsChecker extends BugChecker implements + BugChecker.CompilationUnitTreeMatcher { + + @Override + public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) { + final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree); + final String sourceCode = state.getSourceCode().toString(); + for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) { + for (Tokens.Comment comment : token.comments()) { + if (!javadocableTrees.containsKey(token.pos())) { + continue; + } + generateFix(comment).ifPresent(fix -> { + final Tree javadocableTree = javadocableTrees.get(token.pos()); + state.reportMatch(describeMatch(javadocableTree, fix)); + }); + } + } + // We might have multiple matches, so report them via VisitorState rather than the return + // value from the match function. + return NO_MATCH; + } + + private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) { + final String text = comment.getText(); + if (text.startsWith("/**")) { + return Optional.empty(); + } + + if (!text.contains("@hide")) { + return Optional.empty(); + } + + if (text.startsWith("/*")) { + final int pos = comment.getSourcePos(1); + return Optional.of(SuggestedFix.replace(pos, pos, "*")); + } else if (text.startsWith("//")) { + final int endPos = comment.getSourcePos(text.length() - 1); + final char endChar = text.charAt(text.length() - 1); + String javadocClose = " */"; + if (endChar != ' ') { + javadocClose = endChar + javadocClose; + } + final SuggestedFix fix = SuggestedFix.builder() + .replace(comment.getSourcePos(1), comment.getSourcePos(2), "**") + .replace(endPos, endPos + 1, javadocClose) + .build(); + return Optional.of(fix); + } + + return Optional.empty(); + } + + + private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) { + Map<Integer, Tree> javadoccableTrees = new HashMap<>(); + new SuppressibleTreePathScanner<Void, Void>() { + @Override + public Void visitClass(ClassTree classTree, Void unused) { + javadoccableTrees.put(getStartPosition(classTree), classTree); + return super.visitClass(classTree, null); + } + + @Override + public Void visitMethod(MethodTree methodTree, Void unused) { + // Generated constructors never have comments + if (!ASTHelpers.isGeneratedConstructor(methodTree)) { + javadoccableTrees.put(getStartPosition(methodTree), methodTree); + } + return super.visitMethod(methodTree, null); + } + + @Override + public Void visitVariable(VariableTree variableTree, Void unused) { + ElementKind kind = getSymbol(variableTree).getKind(); + if (kind == ElementKind.FIELD) { + javadoccableTrees.put(getStartPosition(variableTree), variableTree); + } + if (kind == ElementKind.ENUM_CONSTANT) { + javadoccableTrees.put(getStartPosition(variableTree), variableTree); + if (variableTree.getInitializer() instanceof NewClassTree) { + // Skip the generated class definition + ClassTree classBody = + ((NewClassTree) variableTree.getInitializer()).getClassBody(); + if (classBody != null) { + scan(classBody.getMembers(), null); + } + return null; + } + } + return super.visitVariable(variableTree, null); + } + + }.scan(tree, null); + return javadoccableTrees; + } + +} diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java new file mode 100644 index 000000000000..f3e6c72756e0 --- /dev/null +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2022 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.google.errorprone.bugpatterns.android; + +import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH; + +import com.google.errorprone.BugCheckerRefactoringTestHelper; +import com.google.errorprone.CompilationTestHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class HideInCommentsCheckerTest { + private static final String REFACTORING_FILE = "Test.java"; + + private BugCheckerRefactoringTestHelper mRefactoringHelper; + private CompilationTestHelper mCompilationHelper; + + @Before + public void setUp() { + mRefactoringHelper = BugCheckerRefactoringTestHelper.newInstance( + HideInCommentsChecker.class, HideInCommentsCheckerTest.class); + mCompilationHelper = CompilationTestHelper.newInstance( + HideInCommentsChecker.class, HideInCommentsCheckerTest.class); + } + + + @Test + public void refactorSingleLineComment() { + mRefactoringHelper + .addInputLines( + REFACTORING_FILE, + "public class Test {", + " // Foo @hide", + " void foo() {}", + "}") + .addOutputLines( + REFACTORING_FILE, + "public class Test {", + " /** Foo @hide */", + " void foo() {}", + "}") + .doTest(TEXT_MATCH); + } + + @Test + public void refactorSingleLineComment_doesntAddUnnecessarySpace() { + mRefactoringHelper + .addInputLines( + REFACTORING_FILE, + "public class Test {", + " // Foo @hide ", + " void foo() {}", + "}") + .addOutputLines( + REFACTORING_FILE, + "public class Test {", + " /** Foo @hide */", + " void foo() {}", + "}") + .doTest(TEXT_MATCH); + } + + @Test + public void refactorSingleLineBlockComment() { + mRefactoringHelper + .addInputLines( + REFACTORING_FILE, + "public class Test {", + " /* Foo @hide */", + " void foo() {}", + "}") + .addOutputLines( + REFACTORING_FILE, + "public class Test {", + " /** Foo @hide */", + " void foo() {}", + "}") + .doTest(TEXT_MATCH); + } + + @Test + public void refactorMultiLineBlockComment() { + mRefactoringHelper + .addInputLines( + REFACTORING_FILE, + "public class Test {", + " /*", + " * Foo.", + " *", + " * @hide", + " */", + " void foo(int foo) {}", + "}") + .addOutputLines( + REFACTORING_FILE, + "public class Test {", + " /**", + " * Foo.", + " *", + " * @hide", + " */", + " void foo(int foo) {}", + "}") + .doTest(TEXT_MATCH); + } + + @Test + public void refactorFieldComment() { + mRefactoringHelper + .addInputLines( + REFACTORING_FILE, + "public class Test {", + " /* Foo @hide */", + " public int foo = 0;", + "}") + .addOutputLines( + REFACTORING_FILE, + "public class Test {", + " /** Foo @hide */", + " public int foo = 0;", + "}") + .doTest(TEXT_MATCH); + } + + @Test + public void refactorClassComment() { + mRefactoringHelper + .addInputLines( + REFACTORING_FILE, + "/* Foo @hide */", + "public class Test {}") + .addOutputLines( + REFACTORING_FILE, + "/** Foo @hide */", + "public class Test {}") + .doTest(TEXT_MATCH); + } + + @Test + public void refactorEnumComment() { + mRefactoringHelper + .addInputLines( + REFACTORING_FILE, + "public enum Test {", + " /* Foo @hide */", + " FOO", + "}") + .addOutputLines( + REFACTORING_FILE, + "public enum Test {", + " /** Foo @hide */", + " FOO", + "}") + .doTest(TEXT_MATCH); + } + + @Test + public void canBeSuppressed() { + mCompilationHelper + .addSourceLines( + REFACTORING_FILE, + "public class Test {", + " /* Foo @hide */", + " @SuppressWarnings(\"AndroidHideInComments\")", + " void foo() {}", + "}") + .doTest(); + } + + @Test + public void isInJavadoc() { + mCompilationHelper + .addSourceLines( + REFACTORING_FILE, + "public class Test {", + " /** Foo @hide */", + " void foo() {}", + "}") + .doTest(); + } + + @Test + public void isInMultilineJavadoc() { + mCompilationHelper + .addSourceLines( + REFACTORING_FILE, + "public class Test {", + " /**", + " * Foo.", + " *", + " * @hide", + " */", + " void foo(int foo) {}", + "}") + .doTest(); + } + + @Test + public void noHidePresent() { + mCompilationHelper + .addSourceLines( + "test/" + REFACTORING_FILE, + "package test;", + "// Foo.", + "public class Test {", + " // Foo.", + " public int a;", + " /*", + " * Foo.", + " *", + " */", + " void foo(int foo) {}", + "}") + .doTest(); + } + +} diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java index d083e444e996..7ad9aecaf6a3 100644 --- a/graphics/java/android/graphics/text/LineBreakConfig.java +++ b/graphics/java/android/graphics/text/LineBreakConfig.java @@ -89,6 +89,11 @@ public final class LineBreakConfig { private @LineBreakWordStyle int mLineBreakWordStyle = LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE; + // Whether or not enabling phrase breaking automatically. + // TODO(b/226012260): Remove this and add LINE_BREAK_WORD_STYLE_PHRASE_AUTO after + // the experiment. + private boolean mAutoPhraseBreaking = false; + /** * Builder constructor with line break parameters. */ @@ -118,12 +123,22 @@ public final class LineBreakConfig { } /** + * Enable or disable the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE}. + * + * @hide + */ + public @NonNull Builder setAutoPhraseBreaking(boolean autoPhraseBreaking) { + mAutoPhraseBreaking = autoPhraseBreaking; + return this; + } + + /** * Build the {@link LineBreakConfig} * * @return the LineBreakConfig instance. */ public @NonNull LineBreakConfig build() { - return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle); + return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle, mAutoPhraseBreaking); } } @@ -143,6 +158,23 @@ public final class LineBreakConfig { .build(); } + /** + * Create the LineBreakConfig instance. + * + * @param lineBreakStyle the line break style for text wrapping. + * @param lineBreakWordStyle the line break word style for text wrapping. + * @return the {@link LineBreakConfig} instance. * + * @hide + */ + public static @NonNull LineBreakConfig getLineBreakConfig(@LineBreakStyle int lineBreakStyle, + @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) { + LineBreakConfig.Builder builder = new LineBreakConfig.Builder(); + return builder.setLineBreakStyle(lineBreakStyle) + .setLineBreakWordStyle(lineBreakWordStyle) + .setAutoPhraseBreaking(autoPhraseBreaking) + .build(); + } + /** @hide */ public static final LineBreakConfig NONE = new Builder().setLineBreakStyle(LINE_BREAK_STYLE_NONE) @@ -150,15 +182,17 @@ public final class LineBreakConfig { private final @LineBreakStyle int mLineBreakStyle; private final @LineBreakWordStyle int mLineBreakWordStyle; + private final boolean mAutoPhraseBreaking; /** * Constructor with the line break parameters. * Use the {@link LineBreakConfig.Builder} to create the LineBreakConfig instance. */ private LineBreakConfig(@LineBreakStyle int lineBreakStyle, - @LineBreakWordStyle int lineBreakWordStyle) { + @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) { mLineBreakStyle = lineBreakStyle; mLineBreakWordStyle = lineBreakWordStyle; + mAutoPhraseBreaking = autoPhraseBreaking; } /** @@ -179,6 +213,17 @@ public final class LineBreakConfig { return mLineBreakWordStyle; } + /** + * Used to identify if the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled. + * + * @return The result that records whether or not the automation of + * {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled. + * @hide + */ + public boolean getAutoPhraseBreaking() { + return mAutoPhraseBreaking; + } + @Override public boolean equals(Object o) { if (o == null) return false; @@ -186,7 +231,8 @@ public final class LineBreakConfig { if (!(o instanceof LineBreakConfig)) return false; LineBreakConfig that = (LineBreakConfig) o; return (mLineBreakStyle == that.mLineBreakStyle) - && (mLineBreakWordStyle == that.mLineBreakWordStyle); + && (mLineBreakWordStyle == that.mLineBreakWordStyle) + && (mAutoPhraseBreaking == that.mAutoPhraseBreaking); } @Override diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java index 8e011053d2a7..c591c879059c 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java @@ -38,8 +38,9 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.time.Instant; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; +import java.util.List; import java.util.Map; import javax.crypto.BadPaddingException; @@ -227,7 +228,7 @@ class CredstoreIdentityCredential extends IdentityCredential { throw new RuntimeException("Error decoding certificates", e); } - LinkedList<X509Certificate> x509Certs = new LinkedList<>(); + ArrayList<X509Certificate> x509Certs = new ArrayList<>(); for (Certificate cert : certs) { x509Certs.add((X509Certificate) cert); } @@ -384,7 +385,7 @@ class CredstoreIdentityCredential extends IdentityCredential { public @NonNull Collection<X509Certificate> getAuthKeysNeedingCertification() { try { AuthKeyParcel[] authKeyParcels = mBinder.getAuthKeysNeedingCertification(); - LinkedList<X509Certificate> x509Certs = new LinkedList<>(); + ArrayList<X509Certificate> x509Certs = new ArrayList<>(); CertificateFactory factory = CertificateFactory.getInstance("X.509"); for (AuthKeyParcel authKeyParcel : authKeyParcels) { Collection<? extends Certificate> certs = null; diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java index d2e7984ce19f..1ad70edb7c6f 100644 --- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java @@ -25,8 +25,9 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; +import java.util.List; class CredstoreWritableIdentityCredential extends WritableIdentityCredential { @@ -61,7 +62,7 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential { throw new RuntimeException("Error decoding certificates", e); } - LinkedList<X509Certificate> x509Certs = new LinkedList<>(); + ArrayList<X509Certificate> x509Certs = new ArrayList<>(); for (Certificate cert : certs) { x509Certs.add((X509Certificate) cert); } diff --git a/identity/java/android/security/identity/PersonalizationData.java b/identity/java/android/security/identity/PersonalizationData.java index b34f2505a6a6..bdb00fdfb341 100644 --- a/identity/java/android/security/identity/PersonalizationData.java +++ b/identity/java/android/security/identity/PersonalizationData.java @@ -18,10 +18,11 @@ package android.security.identity; import android.annotation.NonNull; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.LinkedList; +import java.util.List; /** * An object that holds personalization data. @@ -38,7 +39,7 @@ public class PersonalizationData { private PersonalizationData() { } - private LinkedList<AccessControlProfile> mProfiles = new LinkedList<>(); + private ArrayList<AccessControlProfile> mProfiles = new ArrayList<>(); private LinkedHashMap<String, NamespaceData> mNamespaces = new LinkedHashMap<>(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 31f0ef0192ae..b20caf4b46bb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -439,6 +439,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } notifyLocusVisibilityIfNeeded(info.getTaskInfo()); notifyCompatUI(info.getTaskInfo(), listener); + mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskAdded(info.getTaskInfo())); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl index 6e78fcba4a00..b71cc32a0347 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl @@ -16,6 +16,8 @@ package com.android.wm.shell.recents; +import android.app.ActivityManager; + import com.android.wm.shell.recents.IRecentTasksListener; import com.android.wm.shell.util.GroupedRecentTaskInfo; @@ -38,4 +40,9 @@ interface IRecentTasks { * Gets the set of recent tasks. */ GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3; + + /** + * Gets the set of running tasks. + */ + ActivityManager.RunningTaskInfo[] getRunningTasks(int maxNum) = 4; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl index 8efa42830d80..59f72335678e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl @@ -16,6 +16,8 @@ package com.android.wm.shell.recents; +import android.app.ActivityManager; + /** * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks. */ @@ -25,4 +27,14 @@ oneway interface IRecentTasksListener { * Called when the set of recent tasks change. */ void onRecentTasksChanged(); + + /** + * Called when a running task appears. + */ + void onRunningTaskAppeared(in ActivityManager.RunningTaskInfo taskInfo); + + /** + * Called when a running task vanishes. + */ + void onRunningTaskVanished(in ActivityManager.RunningTaskInfo taskInfo); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index c166178e9bbd..d903d5b33780 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -17,6 +17,7 @@ package com.android.wm.shell.recents; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.content.pm.PackageManager.FEATURE_PC; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; @@ -63,8 +64,9 @@ public class RecentTasksController implements TaskStackListenerCallback, private final ShellExecutor mMainExecutor; private final TaskStackListenerImpl mTaskStackListener; private final RecentTasks mImpl = new RecentTasksImpl(); + private IRecentTasksListener mListener; + private final boolean mIsDesktopMode; - private final ArrayList<Runnable> mCallbacks = new ArrayList<>(); // Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a // pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1) private final SparseIntArray mSplitTasks = new SparseIntArray(); @@ -95,6 +97,7 @@ public class RecentTasksController implements TaskStackListenerCallback, RecentTasksController(Context context, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { mContext = context; + mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); mTaskStackListener = taskStackListener; mMainExecutor = mainExecutor; } @@ -176,10 +179,15 @@ public class RecentTasksController implements TaskStackListenerCallback, notifyRecentTasksChanged(); } - public void onTaskRemoved(TaskInfo taskInfo) { + public void onTaskAdded(ActivityManager.RunningTaskInfo taskInfo) { + notifyRunningTaskAppeared(taskInfo); + } + + public void onTaskRemoved(ActivityManager.RunningTaskInfo taskInfo) { // Remove any split pairs associated with this task removeSplitPair(taskInfo.taskId); notifyRecentTasksChanged(); + notifyRunningTaskVanished(taskInfo); } public void onTaskWindowingModeChanged(TaskInfo taskInfo) { @@ -189,19 +197,50 @@ public class RecentTasksController implements TaskStackListenerCallback, @VisibleForTesting void notifyRecentTasksChanged() { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed"); - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).run(); + if (mListener == null) { + return; + } + try { + mListener.onRecentTasksChanged(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed call notifyRecentTasksChanged", e); } } - private void registerRecentTasksListener(Runnable listener) { - if (!mCallbacks.contains(listener)) { - mCallbacks.add(listener); + /** + * Notify the running task listener that a task appeared on desktop environment. + */ + private void notifyRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { + if (mListener == null || !mIsDesktopMode || taskInfo.realActivity == null) { + return; + } + try { + mListener.onRunningTaskAppeared(taskInfo); + } catch (RemoteException e) { + Slog.w(TAG, "Failed call onRunningTaskAppeared", e); + } + } + + /** + * Notify the running task listener that a task was removed on desktop environment. + */ + private void notifyRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + if (mListener == null || !mIsDesktopMode || taskInfo.realActivity == null) { + return; + } + try { + mListener.onRunningTaskVanished(taskInfo); + } catch (RemoteException e) { + Slog.w(TAG, "Failed call onRunningTaskVanished", e); } } - private void unregisterRecentTasksListener(Runnable listener) { - mCallbacks.remove(listener); + private void registerRecentTasksListener(IRecentTasksListener listener) { + mListener = listener; + } + + private void unregisterRecentTasksListener() { + mListener = null; } @VisibleForTesting @@ -280,19 +319,28 @@ public class RecentTasksController implements TaskStackListenerCallback, private RecentTasksController mController; private final SingleInstanceRemoteListener<RecentTasksController, IRecentTasksListener> mListener; - private final Runnable mRecentTasksListener = - new Runnable() { - @Override - public void run() { - mListener.call(l -> l.onRecentTasksChanged()); - } - }; + private final IRecentTasksListener mRecentTasksListener = new IRecentTasksListener.Stub() { + @Override + public void onRecentTasksChanged() throws RemoteException { + mListener.call(l -> l.onRecentTasksChanged()); + } + + @Override + public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { + mListener.call(l -> l.onRunningTaskAppeared(taskInfo)); + } + + @Override + public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + mListener.call(l -> l.onRunningTaskVanished(taskInfo)); + } + }; public IRecentTasksImpl(RecentTasksController controller) { mController = controller; mListener = new SingleInstanceRemoteListener<>(controller, c -> c.registerRecentTasksListener(mRecentTasksListener), - c -> c.unregisterRecentTasksListener(mRecentTasksListener)); + c -> c.unregisterRecentTasksListener()); } /** @@ -331,5 +379,16 @@ public class RecentTasksController implements TaskStackListenerCallback, true /* blocking */); return out[0]; } + + @Override + public ActivityManager.RunningTaskInfo[] getRunningTasks(int maxNum) { + final ActivityManager.RunningTaskInfo[][] tasks = + new ActivityManager.RunningTaskInfo[][] {null}; + executeRemoteCallWithTaskPermission(mController, "getRunningTasks", + (controller) -> tasks[0] = ActivityTaskManager.getInstance().getTasks(maxNum) + .toArray(new ActivityManager.RunningTaskInfo[0]), + true /* blocking */); + return tasks[0]; + } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index 50f6bd7b4927..9ef8c322d105 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -30,11 +30,13 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static java.lang.Integer.MAX_VALUE; import android.app.ActivityManager; import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.Rect; import android.view.SurfaceControl; @@ -77,6 +79,7 @@ public class RecentTasksControllerTest extends ShellTestCase { @Before public void setUp() { mMainExecutor = new TestShellExecutor(); + when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); mRecentTasksController = spy(new RecentTasksController(mContext, mTaskStackListener, mMainExecutor)); mShellTaskOrganizer = new ShellTaskOrganizer(mMainExecutor, mContext, diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index c80fb188e70f..8a379d581532 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -33,6 +33,7 @@ license { cc_defaults { name: "libandroidfw_defaults", + cpp_std: "gnu++2b", cflags: [ "-Werror", "-Wunreachable-code", diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h index 921877dc4982..fac2fa4fa575 100644 --- a/libs/androidfw/include/androidfw/StringPiece.h +++ b/libs/androidfw/include/androidfw/StringPiece.h @@ -288,12 +288,12 @@ inline ::std::basic_string<TChar>& operator+=(::std::basic_string<TChar>& lhs, template <typename TChar> inline bool operator==(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) { - return rhs == lhs; + return BasicStringPiece<TChar>(lhs) == rhs; } template <typename TChar> inline bool operator!=(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) { - return rhs != lhs; + return BasicStringPiece<TChar>(lhs) != rhs; } } // namespace android diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index d5fee3f667a9..2e6e36a9ff22 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -16,7 +16,9 @@ #ifndef DEVICEINFO_H #define DEVICEINFO_H +#include <SkColorSpace.h> #include <SkImageInfo.h> +#include <SkRefCnt.h> #include <android/data_space.h> #include <mutex> diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index c24cabb287de..7291cab364e2 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -22,8 +22,11 @@ #include <GLES2/gl2ext.h> #include <GLES3/gl3.h> #include <GrDirectContext.h> +#include <SkBitmap.h> #include <SkCanvas.h> #include <SkImage.h> +#include <SkImageInfo.h> +#include <SkRefCnt.h> #include <gui/TraceUtils.h> #include <utils/GLUtils.h> #include <utils/NdkUtils.h> diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h index 81057a24c29c..00ee99648889 100644 --- a/libs/hwui/HardwareBitmapUploader.h +++ b/libs/hwui/HardwareBitmapUploader.h @@ -17,6 +17,9 @@ #pragma once #include <hwui/Bitmap.h> +#include <SkRefCnt.h> + +class SkBitmap; namespace android::uirenderer { diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 4cce87ad1a2f..79953aa6adc9 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -26,6 +26,18 @@ #include "pipeline/skia/LayerDrawable.h" #include "renderthread/EglManager.h" #include "renderthread/VulkanManager.h" +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColorSpace.h> +#include <SkImage.h> +#include <SkImageInfo.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkRect.h> +#include <SkRefCnt.h> +#include <SkSamplingOptions.h> +#include <SkSurface.h> #include "utils/Color.h" #include "utils/MathUtils.h" #include "utils/NdkUtils.h" diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index d0d748ff5c16..aa6e43c3bc27 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -20,7 +20,11 @@ #include "Rect.h" #include "renderthread/RenderThread.h" -#include <SkBitmap.h> +#include <SkRefCnt.h> + +class SkBitmap; +class SkImage; +struct SkRect; namespace android { class Bitmap; diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index a285462eef74..f5ebfd5d9e23 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -29,10 +29,14 @@ #include "SkDrawShadowInfo.h" #include "SkImage.h" #include "SkImageFilter.h" +#include "SkImageInfo.h" #include "SkLatticeIter.h" #include "SkMath.h" +#include "SkPaint.h" #include "SkPicture.h" +#include "SkRRect.h" #include "SkRSXform.h" +#include "SkRect.h" #include "SkRegion.h" #include "SkTextBlob.h" #include "SkVertices.h" diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 212b4e72dcb2..35bec9335d7c 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -34,6 +34,8 @@ #include <SkRuntimeEffect.h> #include <vector> +class SkRRect; + namespace android { namespace uirenderer { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 53c6db0cdf3a..023d6bf0b673 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -27,6 +27,7 @@ #include <SkAndroidFrameworkUtils.h> #include <SkAnimatedImage.h> +#include <SkBitmap.h> #include <SkCanvasPriv.h> #include <SkCanvasStateUtils.h> #include <SkColorFilter.h> @@ -36,8 +37,13 @@ #include <SkGraphics.h> #include <SkImage.h> #include <SkImagePriv.h> +#include <SkMatrix.h> +#include <SkPaint.h> #include <SkPicture.h> #include <SkRSXform.h> +#include <SkRRect.h> +#include <SkRect.h> +#include <SkRefCnt.h> #include <SkShader.h> #include <SkTemplates.h> #include <SkTextBlob.h> diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 715007cdcd3b..c6313f6c3a88 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -33,6 +33,8 @@ #include <cassert> #include <optional> +class SkRRect; + namespace android { // Holds an SkCanvas reference plus additional native data. diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 983c7766273a..536ff781badc 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -21,9 +21,10 @@ #include <utils/Log.h> #include "PathParser.h" -#include "SkColorFilter.h" +#include "SkImage.h" #include "SkImageInfo.h" -#include "SkShader.h" +#include "SkSamplingOptions.h" +#include "SkScalar.h" #include "hwui/Paint.h" #ifdef __ANDROID__ diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index 30bb04ae8361..c92654c479c1 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -31,6 +31,7 @@ #include <SkPath.h> #include <SkPathMeasure.h> #include <SkRect.h> +#include <SkRefCnt.h> #include <SkShader.h> #include <SkSurface.h> diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp index bc6bc456ba5a..c442a7b1d17c 100644 --- a/libs/hwui/apex/android_bitmap.cpp +++ b/libs/hwui/apex/android_bitmap.cpp @@ -24,6 +24,11 @@ #include <GraphicsJNI.h> #include <hwui/Bitmap.h> +#include <SkBitmap.h> +#include <SkColorSpace.h> +#include <SkImageInfo.h> +#include <SkRefCnt.h> +#include <SkStream.h> #include <utils/Color.h> using namespace android; diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp index 2a939efed9bb..905b123076a2 100644 --- a/libs/hwui/apex/android_canvas.cpp +++ b/libs/hwui/apex/android_canvas.cpp @@ -23,7 +23,9 @@ #include <utils/Color.h> #include <SkBitmap.h> +#include <SkColorSpace.h> #include <SkSurface.h> +#include <SkRefCnt.h> using namespace android; diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index fdc97a4fd8ba..2dcbca8273e7 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -17,13 +17,19 @@ #pragma once #include <SkAndroidFrameworkUtils.h> +#include <SkBlendMode.h> #include <SkCanvas.h> -#include <SkPath.h> -#include <SkRegion.h> -#include <SkVertices.h> +#include <SkClipOp.h> #include <SkImage.h> +#include <SkPaint.h> +#include <SkPath.h> #include <SkPicture.h> +#include <SkRRect.h> +#include <SkRect.h> +#include <SkRegion.h> #include <SkRuntimeEffect.h> +#include <SkSamplingOptions.h> +#include <SkVertices.h> #include <log/log.h> diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 67f47580a70f..feafc2372442 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -35,9 +35,15 @@ #endif #include <SkCanvas.h> +#include <SkColor.h> +#include <SkEncodedImageFormat.h> +#include <SkHighContrastFilter.h> +#include <SkImageEncoder.h> #include <SkImagePriv.h> +#include <SkPixmap.h> +#include <SkRect.h> +#include <SkStream.h> #include <SkWebpEncoder.h> -#include <SkHighContrastFilter.h> #include <limits> namespace android { diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 94a047c06ced..133f1fe0a1e7 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -19,9 +19,9 @@ #include <SkColorFilter.h> #include <SkColorSpace.h> #include <SkImage.h> -#include <SkImage.h> #include <SkImageInfo.h> #include <SkPixelRef.h> +#include <SkRefCnt.h> #include <cutils/compiler.h> #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration #include <android/hardware_buffer.h> diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp index 270d24af99fd..d4b0198d015d 100644 --- a/libs/hwui/hwui/BlurDrawLooper.cpp +++ b/libs/hwui/hwui/BlurDrawLooper.cpp @@ -15,6 +15,7 @@ */ #include "BlurDrawLooper.h" +#include <SkColorSpace.h> #include <SkMaskFilter.h> namespace android { diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index b046f45d9c57..cd8af3d933b1 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -26,6 +26,7 @@ #include "hwui/PaintFilter.h" #include <SkFontMetrics.h> +#include <SkRRect.h> namespace android { diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 82777646f3a2..7378351ef771 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -31,6 +31,7 @@ class SkAnimatedImage; class SkCanvasState; +class SkRRect; class SkRuntimeShaderBuilder; class SkVertices; diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index cef2233fc371..b6d73b39d8d0 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -17,9 +17,11 @@ #include <SkAndroidCodec.h> #include <SkCodec.h> +#include <SkColorSpace.h> #include <SkImageInfo.h> #include <SkPngChunkReader.h> #include <SkRect.h> +#include <SkRefCnt.h> #include <SkSize.h> #include <cutils/compiler.h> diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index 2db3ace1cd43..34cb4aef70d9 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -16,10 +16,13 @@ #include "MinikinSkia.h" -#include <SkFontDescriptor.h> #include <SkFont.h> +#include <SkFontDescriptor.h> #include <SkFontMetrics.h> #include <SkFontMgr.h> +#include <SkRect.h> +#include <SkScalar.h> +#include <SkStream.h> #include <SkTypeface.h> #include <log/log.h> diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index c40b858268be..373e893b9a25 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -21,8 +21,11 @@ #include <SkAndroidCodec.h> #include <SkAnimatedImage.h> #include <SkColorFilter.h> +#include <SkEncodedImageFormat.h> #include <SkPicture.h> #include <SkPictureRecorder.h> +#include <SkRect.h> +#include <SkRefCnt.h> #include <hwui/AnimatedImageDrawable.h> #include <hwui/ImageDecoder.h> #include <hwui/Canvas.h> diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 5db0783cf83e..94cea65897cf 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -2,17 +2,25 @@ #define LOG_TAG "Bitmap" #include "Bitmap.h" +#include "GraphicsJNI.h" #include "SkBitmap.h" +#include "SkBlendMode.h" #include "SkCanvas.h" #include "SkColor.h" #include "SkColorSpace.h" -#include "SkPixelRef.h" +#include "SkData.h" #include "SkImageEncoder.h" #include "SkImageInfo.h" -#include "GraphicsJNI.h" +#include "SkPaint.h" +#include "SkPixelRef.h" +#include "SkPixmap.h" +#include "SkPoint.h" +#include "SkRefCnt.h" #include "SkStream.h" +#include "SkTypes.h" #include "SkWebpEncoder.h" + #include "android_nio_utils.h" #include "CreateJavaOutputStreamAdaptor.h" #include <hwui/Paint.h> diff --git a/libs/hwui/jni/Bitmap.h b/libs/hwui/jni/Bitmap.h index 73eca3aa8ef8..21a93f066d9b 100644 --- a/libs/hwui/jni/Bitmap.h +++ b/libs/hwui/jni/Bitmap.h @@ -19,7 +19,6 @@ #include <jni.h> #include <android/bitmap.h> -class SkBitmap; struct SkImageInfo; namespace android { diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index 4e9daa4b0c16..320d3322904f 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -8,9 +8,19 @@ #include "MimeType.h" #include "NinePatchPeeker.h" #include "SkAndroidCodec.h" +#include "SkBitmap.h" +#include "SkBlendMode.h" #include "SkCanvas.h" +#include "SkColorSpace.h" +#include "SkEncodedImageFormat.h" +#include "SkImageInfo.h" #include "SkMath.h" +#include "SkPaint.h" #include "SkPixelRef.h" +#include "SkRect.h" +#include "SkRefCnt.h" +#include "SkSamplingOptions.h" +#include "SkSize.h" #include "SkStream.h" #include "SkString.h" #include "SkUtils.h" diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 1c20415dcc8f..eb56ae310231 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -25,6 +25,7 @@ #include "BitmapRegionDecoder.h" #include "SkBitmap.h" #include "SkCodec.h" +#include "SkColorSpace.h" #include "SkData.h" #include "SkStream.h" diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp index b10540cb3fbd..97dbc9ac171f 100644 --- a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp @@ -2,6 +2,7 @@ #include "GraphicsJNI.h" #include "Utils.h" +#include <SkData.h> #include <SkStream.h> using namespace android; diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index ce5ac382aeff..acc1b0424030 100644 --- a/libs/hwui/jni/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -24,6 +24,7 @@ #include "SkData.h" #include "SkFontMgr.h" #include "SkRefCnt.h" +#include "SkStream.h" #include "SkTypeface.h" #include "Utils.h" #include "fonts/Font.h" diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp index fef51b8d2f79..ae6ac4ce4ecc 100644 --- a/libs/hwui/jni/GIFMovie.cpp +++ b/libs/hwui/jni/GIFMovie.cpp @@ -7,9 +7,11 @@ #include "Movie.h" +#include "SkBitmap.h" #include "SkColor.h" #include "SkColorPriv.h" #include "SkStream.h" +#include "SkTypes.h" #include "gif_lib.h" diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 33669ac0a34e..6a3bc8fe1152 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -8,10 +8,18 @@ #include <nativehelper/JNIHelp.h> #include "GraphicsJNI.h" +#include "include/private/SkTemplates.h" // SkTAddOffset +#include "SkBitmap.h" #include "SkCanvas.h" +#include "SkColorSpace.h" #include "SkFontMetrics.h" +#include "SkImageInfo.h" #include "SkMath.h" +#include "SkPixelRef.h" +#include "SkPoint.h" +#include "SkRect.h" #include "SkRegion.h" +#include "SkTypes.h" #include <cutils/ashmem.h> #include <hwui/Canvas.h> diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index f7b8c014be6e..bad710dec274 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -29,8 +29,12 @@ #include <FrontBufferedStream.h> #include <SkAndroidCodec.h> -#include <SkEncodedImageFormat.h> +#include <SkBitmap.h> +#include <SkColorSpace.h> +#include <SkImageInfo.h> +#include <SkRect.h> #include <SkStream.h> +#include <SkString.h> #include <androidfw/Asset.h> #include <fcntl.h> diff --git a/libs/hwui/jni/Movie.h b/libs/hwui/jni/Movie.h index 736890d5215e..02113dd58ec8 100644 --- a/libs/hwui/jni/Movie.h +++ b/libs/hwui/jni/Movie.h @@ -13,6 +13,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkRefCnt.h" +#include "SkTypes.h" class SkStreamRewindable; diff --git a/libs/hwui/jni/MovieImpl.cpp b/libs/hwui/jni/MovieImpl.cpp index ae9e04e617b0..abb75fa99c94 100644 --- a/libs/hwui/jni/MovieImpl.cpp +++ b/libs/hwui/jni/MovieImpl.cpp @@ -5,11 +5,12 @@ * found in the LICENSE file. */ #include "Movie.h" -#include "SkCanvas.h" -#include "SkPaint.h" +#include "SkBitmap.h" +#include "SkStream.h" +#include "SkTypes.h" // We should never see this in normal operation since our time values are -// 0-based. So we use it as a sentinal. +// 0-based. So we use it as a sentinel. #define UNINITIALIZED_MSEC ((SkMSec)-1) Movie::Movie() @@ -81,8 +82,6 @@ const SkBitmap& Movie::bitmap() //////////////////////////////////////////////////////////////////// -#include "SkStream.h" - Movie* Movie::DecodeMemory(const void* data, size_t length) { SkMemoryStream stream(data, length, false); return Movie::DecodeStream(&stream); diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp index 08fc80fbdafd..d50a8a22b5cb 100644 --- a/libs/hwui/jni/NinePatch.cpp +++ b/libs/hwui/jni/NinePatch.cpp @@ -24,8 +24,10 @@ #include <hwui/Paint.h> #include <utils/Log.h> +#include "SkBitmap.h" #include "SkCanvas.h" #include "SkLatticeIter.h" +#include "SkRect.h" #include "SkRegion.h" #include "GraphicsJNI.h" #include "NinePatchPeeker.h" diff --git a/libs/hwui/jni/NinePatchPeeker.cpp b/libs/hwui/jni/NinePatchPeeker.cpp index 9171fc687276..d85ede5dc6d2 100644 --- a/libs/hwui/jni/NinePatchPeeker.cpp +++ b/libs/hwui/jni/NinePatchPeeker.cpp @@ -16,7 +16,7 @@ #include "NinePatchPeeker.h" -#include <SkBitmap.h> +#include <SkScalar.h> #include <cutils/compiler.h> using namespace android; diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index f76863255153..0aa14655725c 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -26,6 +26,7 @@ #include <nativehelper/ScopedPrimitiveArray.h> #include "SkColorFilter.h" +#include "SkColorSpace.h" #include "SkFont.h" #include "SkFontMetrics.h" #include "SkFontTypes.h" diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 0bbd8a8cf97c..fa8e2e79c831 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -2,11 +2,21 @@ #define LOG_TAG "ShaderJNI" #include "GraphicsJNI.h" +#include "SkBitmap.h" +#include "SkBlendMode.h" +#include "SkColor.h" #include "SkColorFilter.h" #include "SkGradientShader.h" +#include "SkImage.h" #include "SkImagePriv.h" +#include "SkMatrix.h" +#include "SkPoint.h" +#include "SkRefCnt.h" +#include "SkSamplingOptions.h" +#include "SkScalar.h" #include "SkShader.h" -#include "SkBlendMode.h" +#include "SkString.h" +#include "SkTileMode.h" #include "include/effects/SkRuntimeEffect.h" #include <vector> @@ -16,7 +26,7 @@ using namespace android::uirenderer; /** * By default Skia gradients will interpolate their colors in unpremul space * and then premultiply each of the results. We must set this flag to preserve - * backwards compatiblity by premultiplying the colors of the gradient first, + * backwards compatibility by premultiplying the colors of the gradient first, * and then interpolating between them. */ static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag; diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h index 6cdf44d85a5a..f6e3a0eeaa0e 100644 --- a/libs/hwui/jni/Utils.h +++ b/libs/hwui/jni/Utils.h @@ -17,8 +17,11 @@ #ifndef _ANDROID_GRAPHICS_UTILS_H_ #define _ANDROID_GRAPHICS_UTILS_H_ +#include "SkRefCnt.h" #include "SkStream.h" +class SkData; + #include <jni.h> #include <androidfw/Asset.h> diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp index 77f42ae70268..87eda7e9f96f 100644 --- a/libs/hwui/jni/YuvToJpegEncoder.cpp +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -1,5 +1,7 @@ #include "CreateJavaOutputStreamAdaptor.h" #include "SkJPEGWriteUtility.h" +#include "SkStream.h" +#include "SkTypes.h" #include "YuvToJpegEncoder.h" #include <ui/PixelFormat.h> #include <hardware/hardware.h> diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h index 7e7b935df276..a69726b17e9d 100644 --- a/libs/hwui/jni/YuvToJpegEncoder.h +++ b/libs/hwui/jni/YuvToJpegEncoder.h @@ -1,13 +1,13 @@ #ifndef _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ #define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ -#include "SkTypes.h" -#include "SkStream.h" extern "C" { #include "jpeglib.h" #include "jerror.h" } +class SkWStream; + class YuvToJpegEncoder { public: /** Create an encoder based on the YUV format. diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 0ef80ee10708..61bb66557adc 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -32,10 +32,22 @@ #include "FontUtils.h" #include "Bitmap.h" +#include "SkBitmap.h" +#include "SkBlendMode.h" +#include "SkClipOp.h" +#include "SkColor.h" +#include "SkColorSpace.h" #include "SkGraphics.h" +#include "SkImageInfo.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkRect.h" +#include "SkRefCnt.h" #include "SkRegion.h" -#include "SkVertices.h" #include "SkRRect.h" +#include "SkScalar.h" +#include "SkVertices.h" namespace minikin { class MeasuredText; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index c48448dffdd2..55b1f23d294a 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -23,8 +23,16 @@ #include <Picture.h> #include <Properties.h> #include <RootRenderNode.h> +#include <SkBitmap.h> +#include <SkColorSpace.h> +#include <SkData.h> +#include <SkImage.h> #include <SkImagePriv.h> +#include <SkPicture.h> +#include <SkPixmap.h> #include <SkSerialProcs.h> +#include <SkStream.h> +#include <SkTypeface.h> #include <dlfcn.h> #include <gui/TraceUtils.h> #include <inttypes.h> @@ -451,7 +459,7 @@ struct PictureCaptureState { }; // TODO: This & Multi-SKP & Single-SKP should all be de-duped into -// a single "make a SkPicture serailizable-safe" utility somewhere +// a single "make a SkPicture serializable-safe" utility somewhere class PictureWrapper : public Picture { public: PictureWrapper(sk_sp<SkPicture>&& src, const std::shared_ptr<PictureCaptureState>& state) diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 09be630dc741..2c421f8727a8 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -22,7 +22,10 @@ #include "SkFont.h" #include "SkFontMetrics.h" #include "SkFontMgr.h" +#include "SkRect.h" #include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkStream.h" #include "SkTypeface.h" #include "GraphicsJNI.h" #include <nativehelper/ScopedUtfChars.h> diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h index 3f89c0712407..6a052dbb7cea 100644 --- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h +++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h @@ -19,6 +19,8 @@ #include "RenderNode.h" #include "SkiaDisplayList.h" +class SkRRect; + namespace android { namespace uirenderer { namespace skiapipeline { diff --git a/libs/hwui/pipeline/skia/HolePunch.h b/libs/hwui/pipeline/skia/HolePunch.h index 92c6f7721a08..d0e1ca35049a 100644 --- a/libs/hwui/pipeline/skia/HolePunch.h +++ b/libs/hwui/pipeline/skia/HolePunch.h @@ -17,7 +17,6 @@ #pragma once #include <string> -#include "SkRRect.h" namespace android { namespace uirenderer { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 507d3dcdcde9..3bf2b2e63a47 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -15,7 +15,11 @@ */ #include "RenderNodeDrawable.h" +#include <SkPaint.h> #include <SkPaintFilterCanvas.h> +#include <SkPoint.h> +#include <SkRRect.h> +#include <SkRect.h> #include <gui/TraceUtils.h> #include "RenderNode.h" #include "SkiaDisplayList.h" diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp index e7432ac5f216..ef3f1ca3c41d 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.cpp +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -16,6 +16,7 @@ #include "ShaderCache.h" #include <GrDirectContext.h> +#include <SkData.h> #include <gui/TraceUtils.h> #include <log/log.h> #include <openssl/sha.h> diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h index 4dcc9fb49802..dd57d428ba31 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.h +++ b/libs/hwui/pipeline/skia/ShaderCache.h @@ -17,12 +17,15 @@ #pragma once #include <GrContextOptions.h> +#include <SkRefCnt.h> #include <cutils/compiler.h> #include <memory> #include <mutex> #include <string> #include <vector> +class SkData; + namespace android { class BlobCache; @@ -45,7 +48,7 @@ public: * and puts the ShaderCache into an initialized state, such that it is * able to insert and retrieve entries from the cache. If identity is * non-null and validation fails, the cache is initialized but contains - * no data. If size is less than zero, the cache is initilaized but + * no data. If size is less than zero, the cache is initialized but * contains no data. * * This should be called when HWUI pipeline is initialized. When not in diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index bc386feb2d6f..c546adaaf779 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -16,15 +16,25 @@ #include "SkiaPipeline.h" +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkColorSpace.h> +#include <SkData.h> +#include <SkImage.h> #include <SkImageEncoder.h> #include <SkImageInfo.h> #include <SkImagePriv.h> +#include <SkMatrix.h> #include <SkMultiPictureDocument.h> #include <SkOverdrawCanvas.h> #include <SkOverdrawColorFilter.h> #include <SkPicture.h> #include <SkPictureRecorder.h> +#include <SkRect.h> +#include <SkRefCnt.h> #include <SkSerialProcs.h> +#include <SkStream.h> +#include <SkString.h> #include <SkTypeface.h> #include <android-base/properties.h> #include <unistd.h> diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index bc8a5659dd83..7887d1ae2117 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -16,14 +16,16 @@ #pragma once -#include <SkSurface.h> +#include <SkColorSpace.h> #include <SkDocument.h> #include <SkMultiPictureDocument.h> +#include <SkSurface.h> #include "Lighting.h" #include "hwui/AnimatedImageDrawable.h" #include "renderthread/CanvasContext.h" #include "renderthread/IRenderPipeline.h" +class SkFILEWStream; class SkPictureRecorder; struct SkSharingSerialContext; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 9c51e628e04a..5c6117d86415 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -16,7 +16,20 @@ #include "SkiaRecordingCanvas.h" #include "hwui/Paint.h" +#include <include/private/SkTemplates.h> // SkAutoSTMalloc +#include <SkBlendMode.h> +#include <SkData.h> +#include <SkDrawable.h> +#include <SkImage.h> #include <SkImagePriv.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkPoint.h> +#include <SkRect.h> +#include <SkRefCnt.h> +#include <SkRRect.h> +#include <SkSamplingOptions.h> +#include <SkTypes.h> #include "CanvasTransform.h" #ifdef __ANDROID__ // Layoutlib does not support Layers #include "Layer.h" diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 1445a27e4248..89e3a2c24e1e 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -22,6 +22,11 @@ #include "SkiaDisplayList.h" #include "pipeline/skia/AnimatedDrawables.h" +class SkBitmap; +class SkMatrix; +class SkPaint; +class SkRRect; + namespace android { namespace uirenderer { namespace skiapipeline { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 56d42e013f31..a721be3c60d1 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -22,6 +22,11 @@ #include "renderstate/RenderState.h" +#include "SkRefCnt.h" + +class SkBitmap; +struct SkRect; + namespace android { namespace uirenderer { namespace skiapipeline { diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp index 41e36874b862..33160d05e2d0 100644 --- a/libs/hwui/pipeline/skia/TransformCanvas.cpp +++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp @@ -19,6 +19,10 @@ #include "HolePunch.h" #include "SkData.h" #include "SkDrawable.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkRect.h" +#include "SkRRect.h" using namespace android::uirenderer::skiapipeline; diff --git a/libs/hwui/pipeline/skia/TransformCanvas.h b/libs/hwui/pipeline/skia/TransformCanvas.h index 685b71d017e9..15f0c1abc55a 100644 --- a/libs/hwui/pipeline/skia/TransformCanvas.h +++ b/libs/hwui/pipeline/skia/TransformCanvas.h @@ -19,6 +19,13 @@ #include "SkPaintFilterCanvas.h" #include <effects/StretchEffect.h> +class SkData; +class SkDrawable; +class SkMatrix; +class SkPaint; +enum class SkBlendMode; +struct SkRect; + class TransformCanvas : public SkPaintFilterCanvas { public: TransformCanvas(SkCanvas* target, SkBlendMode blendmode) : diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index fc6b28d2e1ad..b8f8c9267ad8 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -18,6 +18,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <SkColorSpace.h> #include <SkImageInfo.h> #include <SkRect.h> #include <cutils/compiler.h> diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index aceb5a528fc8..0238889ef78f 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -24,6 +24,7 @@ #include "hwui/Bitmap.h" #include "ColorMode.h" +#include <SkColorSpace.h> #include <SkRect.h> #include <utils/RefBase.h> diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index a44b498c81c1..b2ba15cbe526 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -29,6 +29,10 @@ #include "utils/Macros.h" #include "utils/TimeUtils.h" +#include <SkBitmap.h> +#include <SkImage.h> +#include <SkPicture.h> + #include <pthread.h> namespace android { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index ee9efd46e307..bbfeeac19d94 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -17,7 +17,7 @@ #ifndef RENDERPROXY_H_ #define RENDERPROXY_H_ -#include <SkBitmap.h> +#include <SkRefCnt.h> #include <android/native_window.h> #include <cutils/compiler.h> #include <android/surface_control.h> @@ -30,6 +30,10 @@ #include "SwapBehavior.h" #include "hwui/Bitmap.h" +class SkBitmap; +class SkPicture; +class SkImage; + namespace android { class GraphicBuffer; class Surface; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 01b956cb3dd5..3ff4081726f3 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -266,7 +266,7 @@ void RenderThread::requireGlContext() { } mEglManager->initialize(); - sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); + sk_sp<const GrGLInterface> glInterface = GrGLMakeNativeInterface(); LOG_ALWAYS_FATAL_IF(!glInterface.get()); GrContextOptions options; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index b816649edf6e..cba210da3353 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -51,6 +51,9 @@ typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore #include "VulkanSurface.h" #include "private/hwui/DrawVkInfo.h" +#include <SkColorSpace.h> +#include <SkRefCnt.h> + class GrVkExtensions; namespace android { diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index beb71b727f51..26486669e712 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -20,6 +20,7 @@ #include <system/window.h> #include <vulkan/vulkan.h> +#include <SkColorSpace.h> #include <SkRefCnt.h> #include <SkSize.h> diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 491af4336f97..a4890ede8faa 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -26,7 +26,13 @@ #include <renderthread/VulkanManager.h> #include <utils/Unicode.h> +#include "SkCanvas.h" #include "SkColorData.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkPixmap.h" +#include "SkRect.h" +#include "SkSurface.h" #include "SkUnPreMultiply.h" namespace android { diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 5092675a8104..75865c751d52 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -27,10 +27,20 @@ #include <renderstate/RenderState.h> #include <renderthread/RenderThread.h> +#include <SkBitmap.h> +#include <SkColor.h> +#include <SkImageInfo.h> +#include <SkRefCnt.h> + #include <gtest/gtest.h> #include <memory> #include <unordered_map> +class SkCanvas; +class SkMatrix; +class SkPath; +struct SkRect; + namespace android { namespace uirenderer { diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index 03aeb55f129b..a07cdf720b50 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -14,7 +14,17 @@ * limitations under the License. */ -#include <SkImagePriv.h> +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkImage.h> +#include <SkImageInfo.h> +#include <SkPaint.h> +#include <SkRect.h> +#include <SkRefCnt.h> +#include <SkSamplingOptions.h> +#include <SkShader.h> +#include <SkTileMode.h> #include "hwui/Paint.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp index cbdb756b8fa7..de0ef6d595f8 100644 --- a/libs/hwui/tests/common/scenes/HwBitmap565.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp @@ -18,6 +18,12 @@ #include "tests/common/BitmapAllocationTestUtils.h" #include "utils/Color.h" +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkRefCnt.h> + class HwBitmap565; static TestScene::Registrar _HwBitmap565(TestScene::Info{ diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 564354f04674..0d5ca6df9ff3 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -17,6 +17,7 @@ #include "TestSceneBase.h" #include "utils/Color.h" +#include <SkColorSpace.h> #include <SkGradientShader.h> #include <SkImagePriv.h> #include <ui/PixelFormat.h> diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index d031923a112b..4a5d9468cd88 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -17,7 +17,16 @@ #include "TestSceneBase.h" #include "tests/common/TestListViewSceneBase.h" #include "hwui/Paint.h" +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkColor.h> #include <SkFont.h> +#include <SkFontTypes.h> +#include <SkPaint.h> +#include <SkPoint.h> +#include <SkRect.h> +#include <SkRefCnt.h> +#include <SkScalar.h> #include <cstdio> class ListViewAnimation; @@ -48,7 +57,7 @@ class ListViewAnimation : public TestListViewSceneBase { 128 * 3; paint.setColor(bgDark ? Color::White : Color::Grey_700); - SkFont font; + SkFont font; font.setSize(size / 2); char charToShow = 'A' + (rand() % 26); const SkPoint pos = {SkIntToScalar(size / 2), diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp index edadf78db051..070a339a86a1 100644 --- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp +++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp @@ -19,6 +19,10 @@ #include "utils/Color.h" #include "hwui/Paint.h" +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkFont.h> + class MagnifierAnimation; static TestScene::Registrar _Magnifier(TestScene::Info{ diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp index 716d3979bdcb..3caaf8236d8a 100644 --- a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp +++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp @@ -16,6 +16,12 @@ #include "TestSceneBase.h" +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkRect.h> +#include <SkRefCnt.h> + class ReadbackFromHardware; static TestScene::Registrar _SaveLayer(TestScene::Info{ diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp index 1c2507867f6e..27948f8b4b43 100644 --- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp @@ -17,6 +17,11 @@ #include "TestSceneBase.h" #include "utils/Color.h" +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkColor.h> +#include <SkRefCnt.h> + class RecentsAnimation; static TestScene::Registrar _Recents(TestScene::Info{ diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp index e677549b7894..59230a754f4e 100644 --- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp @@ -14,7 +14,15 @@ * limitations under the License. */ +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkColor.h> #include <SkFont.h> +#include <SkFontTypes.h> +#include <SkPaint.h> +#include <SkPoint.h> +#include <SkRefCnt.h> +#include <SkRRect.h> #include <cstdio> #include "TestSceneBase.h" #include "hwui/Paint.h" diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index c6219c485b85..aff8ca1e26c7 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -14,7 +14,12 @@ * limitations under the License. */ +#include "SkBitmap.h" #include "SkBlendMode.h" +#include "SkColorFilter.h" +#include "SkFont.h" +#include "SkImageInfo.h" +#include "SkRefCnt.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" #include "hwui/Paint.h" diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index 2cf3456694b0..d2b1ef91a898 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -23,9 +23,17 @@ #include <tests/common/CallCountingCanvas.h> -#include "SkPictureRecorder.h" +#include "SkBitmap.h" +#include "SkCanvas.h" #include "SkColor.h" +#include "SkImageInfo.h" #include "SkLatticeIter.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPictureRecorder.h" +#include "SkRRect.h" +#include "SkRect.h" +#include "SkRegion.h" #include "pipeline/skia/AnimatedDrawables.h" #include <SkNoDrawCanvas.h> diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp index 7f2e1589ae6c..ec9ab90fa46b 100644 --- a/libs/hwui/tests/unit/EglManagerTests.cpp +++ b/libs/hwui/tests/unit/EglManagerTests.cpp @@ -20,6 +20,8 @@ #include "renderthread/RenderEffectCapabilityQuery.h" #include "tests/common/TestContext.h" +#include <SkColorSpace.h> + using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h index 2a74afc5bb7a..96a0c6114682 100644 --- a/libs/hwui/tests/unit/FatalTestCanvas.h +++ b/libs/hwui/tests/unit/FatalTestCanvas.h @@ -19,6 +19,8 @@ #include <SkCanvas.h> #include <gtest/gtest.h> +class SkRRect; + namespace { class TestCanvasBase : public SkCanvas { diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp index 87981f115763..105fea408736 100644 --- a/libs/hwui/tests/unit/ShaderCacheTests.cpp +++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp @@ -25,6 +25,8 @@ #include <cstdint> #include "FileBlobCache.h" #include "pipeline/skia/ShaderCache.h" +#include <SkData.h> +#include <SkRefCnt.h> using namespace android::uirenderer::skiapipeline; diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index dc1b2e668dd0..c1ddbd36bcfd 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -16,9 +16,14 @@ #include "tests/common/TestUtils.h" +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkColor.h> #include <SkColorMatrixFilter.h> #include <SkColorSpace.h> -#include <SkImagePriv.h> +#include <SkImageInfo.h> +#include <SkPaint.h> +#include <SkPath.h> #include <SkPathOps.h> #include <SkShader.h> #include <gtest/gtest.h> diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index dae3c9435712..50d9f5683a8b 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -18,6 +18,7 @@ #include <hwui/Paint.h> #include <SkCanvasStateUtils.h> +#include <SkColorSpace.h> #include <SkPicture.h> #include <SkPictureRecorder.h> #include <gtest/gtest.h> diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp index 15ecf5831f3a..ced667eb76e5 100644 --- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -17,6 +17,7 @@ #include <VectorDrawable.h> #include <gtest/gtest.h> +#include <SkCanvas.h> #include <SkClipStack.h> #include <SkSurface_Base.h> #include <string.h> diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp index ab23448ab93f..9295a938f397 100644 --- a/libs/hwui/tests/unit/TypefaceTests.cpp +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -21,8 +21,11 @@ #include <sys/stat.h> #include <utils/Log.h> +#include "SkData.h" #include "SkFontMgr.h" +#include "SkRefCnt.h" #include "SkStream.h" +#include "SkTypeface.h" #include "hwui/MinikinSkia.h" #include "hwui/Typeface.h" diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 6d4c57413f00..c1c21bd7dfbf 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -21,6 +21,12 @@ #include "utils/MathUtils.h" #include "utils/VectorDrawableUtils.h" +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkPath.h> +#include <SkRefCnt.h> +#include <SkShader.h> + #include <functional> namespace android { diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index 45da008c3e8e..956101e7a2a4 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -24,12 +24,6 @@ #include <log/log.h> -#include <SkBitmap.h> -#include <SkBlendMode.h> -#include <SkCanvas.h> -#include <SkColor.h> -#include <SkPaint.h> - namespace { // Time to spend fading out the pointer completely. const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 2b809eab4ae4..a5ca49847bb6 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -249,8 +249,7 @@ void SpriteController::doUpdateSprites() { // Pass cursor metadata in the sprite surface so that when Android is running as a // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and // update mouse cursor in the host OS. - t.setMetadata( - update.state.surfaceControl, METADATA_MOUSE_CURSOR, p); + t.setMetadata(update.state.surfaceControl, gui::METADATA_MOUSE_CURSOR, p); } int32_t surfaceLayer = mOverlayLayer + update.state.layer; diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp index f7c685ff8ba6..4ac66c4ffb6a 100644 --- a/libs/input/TouchSpotController.cpp +++ b/libs/input/TouchSpotController.cpp @@ -23,12 +23,6 @@ #include <log/log.h> -#include <SkBitmap.h> -#include <SkBlendMode.h> -#include <SkCanvas.h> -#include <SkColor.h> -#include <SkPaint.h> - namespace { // Time to spend fading out the spot completely. const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms diff --git a/media/aidl/android/media/audio/common/AudioPort.aidl b/media/aidl/android/media/audio/common/AudioPort.aidl index 84675e3a00a6..d32b840ebfe8 100644 --- a/media/aidl/android/media/audio/common/AudioPort.aidl +++ b/media/aidl/android/media/audio/common/AudioPort.aidl @@ -23,8 +23,9 @@ import android.media.audio.common.AudioProfile; import android.media.audio.common.ExtraAudioDescriptor; /** - * Audio port structure describes the capabilities of an audio port - * as well as its current configuration. + * Audio port structure describes the capabilities of an audio port. + * This is a "blueprint" which contains all the possible configurations + * that are supported by the port. * * {@hide} */ diff --git a/media/aidl/android/media/audio/common/AudioPortExt.aidl b/media/aidl/android/media/audio/common/AudioPortExt.aidl index c4681cbb182e..eadc0ab7c7c5 100644 --- a/media/aidl/android/media/audio/common/AudioPortExt.aidl +++ b/media/aidl/android/media/audio/common/AudioPortExt.aidl @@ -34,6 +34,9 @@ union AudioPortExt { AudioPortDeviceExt device; /** Information specific to mix ports. */ AudioPortMixExt mix; - /** Audio session identifier. */ + /** + * NOT USED. Framework audio session identifier. + * Use android.media.AudioPortExtSys.session on the system side. + */ int session; } diff --git a/media/aidl/android/media/audio/common/AudioPortMixExt.aidl b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl index f3613a4ae31f..eb117eca52bd 100644 --- a/media/aidl/android/media/audio/common/AudioPortMixExt.aidl +++ b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl @@ -32,12 +32,12 @@ parcelable AudioPortMixExt { AudioPortMixExtUseCase usecase; /** * Maximum number of input or output streams that can be simultaneously - * opened for this port. + * opened for this port. '0' means 'unlimited'. */ int maxOpenStreamCount; /** * Maximum number of input or output streams that can be simultaneously - * active for this port. + * active for this port. '0' means 'all opened streams'. */ int maxActiveStreamCount; /** Mute duration while changing device, when used for output. */ diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e7eda3ea4552..2c68273f86e7 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1636,8 +1636,10 @@ public class AudioManager { * * @param on set <var>true</var> to turn on speakerphone; * <var>false</var> to turn it off + * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} or + * {@link AudioManager#clearCommunicationDevice()} instead. */ - public void setSpeakerphoneOn(boolean on){ + @Deprecated public void setSpeakerphoneOn(boolean on) { final IAudioService service = getService(); try { service.setSpeakerphoneOn(mICallBack, on); @@ -1650,8 +1652,9 @@ public class AudioManager { * Checks whether the speakerphone is on or off. * * @return true if speakerphone is on, false if it's off + * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead. */ - public boolean isSpeakerphoneOn() { + @Deprecated public boolean isSpeakerphoneOn() { final IAudioService service = getService(); try { return service.isSpeakerphoneOn(); @@ -2717,8 +2720,9 @@ public class AudioManager { * connection is established. * @see #stopBluetoothSco() * @see #ACTION_SCO_AUDIO_STATE_UPDATED + * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} instead. */ - public void startBluetoothSco(){ + @Deprecated public void startBluetoothSco() { final IAudioService service = getService(); try { service.startBluetoothSco(mICallBack, @@ -2761,9 +2765,10 @@ public class AudioManager { * bluetooth SCO audio with {@link #startBluetoothSco()} when finished with the SCO * connection or if connection fails. * @see #startBluetoothSco() + * @deprecated Use {@link AudioManager#clearCommunicationDevice()} instead. */ // Also used for connections started with {@link #startBluetoothScoVirtualCall()} - public void stopBluetoothSco(){ + @Deprecated public void stopBluetoothSco() { final IAudioService service = getService(); try { service.stopBluetoothSco(mICallBack); @@ -2795,8 +2800,9 @@ public class AudioManager { * * @return true if SCO is used for communications; * false if otherwise + * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead. */ - public boolean isBluetoothScoOn() { + @Deprecated public boolean isBluetoothScoOn() { final IAudioService service = getService(); try { return service.isBluetoothScoOn(); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 149c2f471a4c..04d28e7ddd91 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -60,7 +60,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -484,7 +483,7 @@ public final class TvInputManager { private final Object mLock = new Object(); // @GuardedBy("mLock") - private final List<TvInputCallbackRecord> mCallbackRecords = new LinkedList<>(); + private final List<TvInputCallbackRecord> mCallbackRecords = new ArrayList<>(); // A mapping from TV input ID to the state of corresponding input. // @GuardedBy("mLock") diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 43d4605209dc..88ca30ceb8a6 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java @@ -50,8 +50,8 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.Executor; @@ -302,7 +302,7 @@ public final class TvInteractiveAppManager { new SparseArray<>(); // @GuardedBy("mLock") - private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new LinkedList<>(); + private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new ArrayList<>(); // A sequence number for the next session to be created. Should be protected by a lock // {@code mSessionCallbackRecordMap}. diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 18b779fa7c57..e3e200fcd754 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -129,6 +129,7 @@ cc_library_shared { cc_library_shared { name: "libmedia_jni_utils", srcs: [ + ":libgui_frame_event_aidl", "android_media_Utils.cpp", ], diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 1ebdc273931b..c2afc609e947 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -80,7 +80,7 @@ ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const c Surface* surface = static_cast<Surface*>(window); sp<IBinder> parentHandle = surface->getSurfaceControlHandle(); - uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState; + int32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState; sp<SurfaceControl> surfaceControl; if (parentHandle) { surfaceControl = @@ -88,11 +88,8 @@ ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const c // Format is only relevant for buffer queue layers. PIXEL_FORMAT_UNKNOWN /* format */, flags, parentHandle); } else { - surfaceControl = - client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */, - // Format is only relevant for buffer queue layers. - PIXEL_FORMAT_UNKNOWN /* format */, flags, - static_cast<Surface*>(window)); + // deprecated, this should no longer be used + surfaceControl = nullptr; } if (!surfaceControl) { @@ -669,6 +666,8 @@ void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* aSurfaceTransacti AVsyncId vsyncId) { CHECK_NOT_NULL(aSurfaceTransaction); const auto startTime = AChoreographer_getStartTimeNanosForVsyncId(vsyncId); - ASurfaceTransaction_to_Transaction(aSurfaceTransaction) - ->setFrameTimelineInfo({.vsyncId = vsyncId, .startTimeNanos = startTime}); + FrameTimelineInfo ftInfo; + ftInfo.vsyncId = vsyncId; + ftInfo.startTimeNanos = startTime; + ASurfaceTransaction_to_Transaction(aSurfaceTransaction)->setFrameTimelineInfo(ftInfo); } diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index bb25274e3136..cd6ed2391608 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -25,6 +25,12 @@ #include <hwui/ImageDecoder.h> #include <log/log.h> #include <SkAndroidCodec.h> +#include <SkCodec.h> +#include <SkColorSpace.h> +#include <SkImageInfo.h> +#include <SkRect.h> +#include <SkSize.h> +#include <SkStream.h> #include <utils/Color.h> #include <fcntl.h> diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index 8b5d214f7a10..c1fce7c3dd23 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -48,6 +48,14 @@ android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE" android:theme="@style/ChooserActivity"/> + <activity + android:name=".CompanionDeviceDataTransferActivity" + android:exported="true" + android:launchMode="singleInstance" + android:excludeFromRecents="true" + android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE" + android:theme="@style/ChooserActivity"/> + <service android:name=".CompanionDeviceDiscoveryService" android:exported="false" /> diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml index 3f86dd5d000c..ff72a1cb3efd 100644 --- a/packages/CompanionDeviceManager/res/values-af/strings.xml +++ b/packages/CompanionDeviceManager/res/values-af/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string> <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string> <string name="consent_back" msgid="2560683030046918882">"Terug"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Dra programtoestemmings na jou horlosie toe oor"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Om dit makliker te maak om jou horlosie op te stel, sal programme wat gedurende opstelling op jou horlosie geïnstalleer word, dieselfde toestemmings as jou foon gebruik.\n\n Hierdie toestemmings kan toegang tot jou horlosie se mikrofoon en ligging insluit."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml index 0c8504768e73..609ef2eef57f 100644 --- a/packages/CompanionDeviceManager/res/values-am/strings.xml +++ b/packages/CompanionDeviceManager/res/values-am/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string> <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string> <string name="consent_back" msgid="2560683030046918882">"ተመለስ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"የመተግበሪያ ፈቃዶችን ወደ የእጅ ሰዓትዎ ያስተላልፉ"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"የእጅ ሰዓትዎን ማቀናበርን ለማቅለል በማዋቀር ጊዜ በእጅ ሰዓትዎ ላይ የተጫኑ መተግበሪያዎች እንደ ስልክዎ ተመሳሳይ ፈቃዶችን ይጠቀማሉ።\n\n እነዚህ ፈቃዶች የእጅ ሰዓትዎ ማይክሮፎን እና አካባቢ መዳረሻን ሊያካትቱ ይችላሉ።"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml index 4386e6a0314c..dcf3436e0588 100644 --- a/packages/CompanionDeviceManager/res/values-ar/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"السماح"</string> <string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string> <string name="consent_back" msgid="2560683030046918882">"رجوع"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"نقل أذونات التطبيقات إلى ساعتك"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"لتسهيل إعداد ساعتك، فإن التطبيقات التي يتم تثبيتها على ساعتك أثناء الإعداد ستستخدم الأذونات نفسها التي يستخدمها هاتفك.\n\n قد تشتمل هذه الأذونات على الوصول إلى ميكروفون ساعتك وبيانات موقعها الجغرافي."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml index b099d0cb4ef9..614260b3d55f 100644 --- a/packages/CompanionDeviceManager/res/values-as/strings.xml +++ b/packages/CompanionDeviceManager/res/values-as/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string> <string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string> <string name="consent_back" msgid="2560683030046918882">"উভতি যাওক"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"আপোনাৰ ঘড়ীলৈ এপৰ অনুমতিসমূহ স্থানান্তৰ কৰক"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"আপোনাৰ ঘড়ীটো ছেটআপ কৰাটো অধিক সহজ কৰি তুলিবলৈ, এয়া কৰাৰ সময়ত আপোনাৰ ঘড়ীটোত ইনষ্টল কৰি থোৱা এপ্সমূহে আপোনাৰ ফ’নৰ দৰে একেই অনুমতিসমূহ ব্যৱহাৰ কৰিব।\n\n এই অনুমতিসমূহত আপোনাৰ ঘড়ীৰ মাইক্ৰ’ফ’ন আৰু অৱস্থানৰ এক্সেছ অন্তৰ্ভুক্ত হ’ব পাৰে।"</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"এপ্সমূহক <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>ত দিয়াৰ দৰে <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong>তো একে অনুমতি প্ৰদান কৰিবনে?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>ইয়াত <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>ত মাইক্ৰ’ফ’ন, কেমেৰা আৰু অৱস্থানৰ এক্সেছ আৰু অন্য সংবেদশীল অনুমতিসমূহ প্ৰদান কৰাটো অন্তৰ্ভুক্ত হ’ব পাৰে।</p> <p>আপুনি যিকোনো সময়তে <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>ত থকা আপোনাৰ ছেটিঙত এই অনুমতিসমূহ সলনি কৰিব পাৰে।</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml index 1166ca1f4678..fcc0683ee33c 100644 --- a/packages/CompanionDeviceManager/res/values-az/strings.xml +++ b/packages/CompanionDeviceManager/res/values-az/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string> <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string> <string name="consent_back" msgid="2560683030046918882">"Geriyə"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Tətbiq icazələrini saatınıza köçürün"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Saatınızı ayarlamağı asanlaşdırmaq üçün ayarlama zamanı saatınızda quraşdırılmış tətbiqlər telefonunuzla eyni icazələrdən istifadə edəcək.\n\n Bu icazələrə saatınızın mikrofonuna və məkanına giriş daxil ola bilər."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml index 9123e05d912f..466ffefef21b 100644 --- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string> <string name="consent_back" msgid="2560683030046918882">"Nazad"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Prenesite dozvole za aplikacije na sat"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Da bismo pojednostavili podešavanje sata, aplikacije instalirane na satu tokom podešavanja će koristiti iste dozvole kao telefon.\n\n Te dozvole mogu da obuhvataju pristup mikrofonu i lokaciji sata."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml index 32264b431f0c..12e2c85489f9 100644 --- a/packages/CompanionDeviceManager/res/values-be/strings.xml +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string> <string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Перанос дазволаў праграм на ваш гадзіннік"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Для праграм, усталяваных на гадзіннік падчас наладжвання, будуць дзейнічаць тыя самыя дазволы, што і на тэлефоне.\n\n Так гадзіннік можа атрымаць доступ да мікрафона і даных пра месцазнаходжанне."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml index b603cb2b638a..75db90aadbd8 100644 --- a/packages/CompanionDeviceManager/res/values-bg/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string> <string name="consent_no" msgid="2640796915611404382">"Забраняване"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Прехвърляне на разрешенията за приложенията към часовника"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"За по-лесно конфигуриране на часовника ви приложенията, инсталирани на него по време на настройването, ще използват същите разрешения като предоставените на телефона ви.\n\nТе може да включват достъп до микрофона и местоположението на часовника ви."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml index 498ea9a10b9d..805bf7a3fb87 100644 --- a/packages/CompanionDeviceManager/res/values-bn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string> <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string> <string name="consent_back" msgid="2560683030046918882">"ফিরুন"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"অ্যাপকে দেওয়া অনুমতি আপনার ঘড়িতে ট্রান্সফার করুন"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"ঘড়ি আরও সহজে সেট আপ করতে, সেট আপ চলাকালীন আপনার ঘড়িতে ইনস্টল করা অ্যাপ ফোনের মতো একই অনুমতি ব্যবহার করবে।\n\n এইসব অনুমতির মধ্যে আপনার ঘড়ির মাইক্রোফোন ও লোকেশন সম্পর্কে তথ্যের অ্যাক্সেস অন্তর্ভুক্ত থাকতে পারে।"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml index dc7efa0a3650..0da9ff3719b9 100644 --- a/packages/CompanionDeviceManager/res/values-bs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string> <string name="consent_back" msgid="2560683030046918882">"Nazad"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Prijenos odobrenja za aplikaciju na sat"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Radi lakšeg postavljanja sata, aplikacije instalirane na satu tokom postavljanja će koristiti ista odobrenja kao i na telefonu.\n\n Ta odobrenja mogu uključivati pristup mikrofonu i lokaciji sata."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Davanje jednakih dopuštenja aplikacijama na uređaju <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> kao i na uređaju <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>To može uključivati pristup mikrofonu, kameri i lokaciji i druga dopuštenja za osjetljive podatke na uređaju <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>Ta dopuštenja uvijek možete promijeniti u postavkama na uređaju <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index 34fc76a6cf63..65fbf6d99214 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permet"</string> <string name="consent_no" msgid="2640796915611404382">"No permetis"</string> <string name="consent_back" msgid="2560683030046918882">"Enrere"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfereix els permisos de les aplicacions al teu rellotge"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Per facilitar la configuració del rellotge, les aplicacions instal·lades al rellotge durant la configuració utilitzaran els mateixos permisos que al teu telèfon.\n\n Aquests permisos poden incloure l\'accés al micròfon i a la ubicació del rellotge."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml index e8c7b0983d54..4b5a546c8213 100644 --- a/packages/CompanionDeviceManager/res/values-cs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string> <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string> <string name="consent_back" msgid="2560683030046918882">"Zpět"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Přesunout oprávnění aplikací do hodinek"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Abychom vám usnadnili nastavení hodinek, aplikace nainstalované do hodinek během úvodního nastavení budou používat stejná oprávnění jako váš telefon.\n\n Tato oprávnění mohou zahrnovat přístup k mikrofonu a poloze hodinek."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml index df413002ee68..9cd6880b59d0 100644 --- a/packages/CompanionDeviceManager/res/values-da/strings.xml +++ b/packages/CompanionDeviceManager/res/values-da/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string> <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string> <string name="consent_back" msgid="2560683030046918882">"Tilbage"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Overfør apptilladelser til dit ur"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"For at gøre det nemmere at konfigurere dit ur vil de apps, der installeres under konfigurationen, anvende de samme tilladelser som din telefon.\n\n Disse tilladelser kan omfatte adgang til dit urs mikrofon og lokation."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml index 6134390ad5ba..19266fd107a5 100644 --- a/packages/CompanionDeviceManager/res/values-de/strings.xml +++ b/packages/CompanionDeviceManager/res/values-de/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string> <string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string> <string name="consent_back" msgid="2560683030046918882">"Zurück"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"App-Berechtigungen auf Smartwatch übertragen"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Damit sich deine Smartwatch leichter einrichten lässt, erhalten die Apps, die während der Einrichtung auf deiner Smartwatch installiert werden, automatisch die gleichen Berechtigungen wie deine Smartphone-Apps.\n\n Zu diesen Berechtigungen kann der Zugriff auf das Mikrofon und den Standort deiner Smartwatch gehören."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml index 4713c5d6948d..b6108cd6fe5a 100644 --- a/packages/CompanionDeviceManager/res/values-el/strings.xml +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string> <string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string> <string name="consent_back" msgid="2560683030046918882">"Πίσω"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Μεταφορά αδειών εφαρμογών στο ρολόι σας"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Για να είναι πιο εύκολη η ρύθμιση του ρολογιού σας, οι εφαρμογές που εγκαθίστανται στο ρολόι σας κατά τη ρύθμιση, θα χρησιμοποιούν τις ίδιες άδειες με το τηλέφωνό σας.\n\n Στις άδειες ενδέχεται να περιλαμβάνεται άδεια πρόσβασης στο μικρόφωνο και την τοποθεσία του ρολογιού σας."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml index fff1e5e64a05..c4138ef0bae9 100644 --- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfer app permissions to your watch"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"To make it easier to set up your watch, apps installed on your watch during setup will use the same permissions as your phone.\n\n These permissions may include access to your watch’s microphone and location."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> the same permissions as on <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>This may include microphone, camera and location access, and other sensitive permissions on <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>You can change these permissions at any time in your settings on <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml index fff1e5e64a05..c4138ef0bae9 100644 --- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfer app permissions to your watch"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"To make it easier to set up your watch, apps installed on your watch during setup will use the same permissions as your phone.\n\n These permissions may include access to your watch’s microphone and location."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> the same permissions as on <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>This may include microphone, camera and location access, and other sensitive permissions on <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>You can change these permissions at any time in your settings on <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml index fff1e5e64a05..c4138ef0bae9 100644 --- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfer app permissions to your watch"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"To make it easier to set up your watch, apps installed on your watch during setup will use the same permissions as your phone.\n\n These permissions may include access to your watch’s microphone and location."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> the same permissions as on <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>This may include microphone, camera and location access, and other sensitive permissions on <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>You can change these permissions at any time in your settings on <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml index fff1e5e64a05..c4138ef0bae9 100644 --- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfer app permissions to your watch"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"To make it easier to set up your watch, apps installed on your watch during setup will use the same permissions as your phone.\n\n These permissions may include access to your watch’s microphone and location."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> the same permissions as on <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>This may include microphone, camera and location access, and other sensitive permissions on <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>You can change these permissions at any time in your settings on <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml index a9a9631d6157..69a212aa9f98 100644 --- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don’t allow"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfer app permissions to your watch"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"To make it easier to set up your watch, apps installed on your watch during setup will use the same permissions as your phone.\n\n These permissions may include access to your watch’s microphone and location."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> the same permissions as on <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>This may include Microphone, Camera, and Location access, and other sensitive permissions on <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>You can change these permissions any time in your Settings on <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml index 60b136ae1bf2..12a8006eeb75 100644 --- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"No permitir"</string> <string name="consent_back" msgid="2560683030046918882">"Atrás"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfiere los permisos de la app a tu reloj"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Para que sea más fácil configurar tu reloj, las apps que se instalen en este durante la configuración usarán los mismos permisos que tu teléfono.\n\n Es posible que estos permisos incluyan el acceso al micrófono y a la ubicación del reloj."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml index bcf6621ed5b6..8293213de946 100644 --- a/packages/CompanionDeviceManager/res/values-es/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"No permitir"</string> <string name="consent_back" msgid="2560683030046918882">"Atrás"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferir permisos de aplicaciones a tu reloj"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Para configurar fácilmente tu reloj, las aplicaciones que instales en él durante la configuración usarán los mismos permisos que tengan en tu teléfono.\n\n Estos permisos pueden incluir acceso al micrófono y a la ubicación del reloj."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml index 49483ec46a54..a5249e85caf5 100644 --- a/packages/CompanionDeviceManager/res/values-et/strings.xml +++ b/packages/CompanionDeviceManager/res/values-et/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Luba"</string> <string name="consent_no" msgid="2640796915611404382">"Ära luba"</string> <string name="consent_back" msgid="2560683030046918882">"Tagasi"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Rakenduste lubade kellale ülekandmine"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Selleks et muuta kella seadistamine lihtsamaks, kasutavad teie kellas seadistamise ajal installitud rakendused samasid lubasid, mis neile telefonis antud on.\n\n Need load võivad hõlmata juurdepääsuluba kella mikrofonile ja asukohale."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml index ec0ac2001449..156647981af4 100644 --- a/packages/CompanionDeviceManager/res/values-eu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string> <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string> <string name="consent_back" msgid="2560683030046918882">"Atzera"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferitu aplikazio-baimenak erlojura"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Erlojua errazago konfiguratzeko, konfigurazio-prozesua abian zen bitartean erlojuan instalatutako aplikazioek telefonoak darabiltzan baimen berak erabiliko dituzte.\n\n Baliteke baimen horien artean erlojuaren mikrofonoa eta kokapena atzitzeko baimenak egotea."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml index 663d7c5d9cc5..e0f7d53f0c73 100644 --- a/packages/CompanionDeviceManager/res/values-fa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"مجاز است"</string> <string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string> <string name="consent_back" msgid="2560683030046918882">"برگشت"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"انتقال اجازههای برنامه به ساعت"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"برای آسانتر کردن راهاندازی ساعت، برنامههای نصبشده در ساعت درحین راهاندازی از همان اجازههای تلفن استفاده خواهند کرد.\n\n ممکن است این اجازهها شامل دسترسی به میکروفون و مکان ساعت باشد."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml index b10dc6951e98..e18f634ebd28 100644 --- a/packages/CompanionDeviceManager/res/values-fi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Salli"</string> <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string> <string name="consent_back" msgid="2560683030046918882">"Takaisin"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Siirrä sovellusluvat kelloon"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Sovellukset, jotka on asennettu kelloon käyttöönoton aikana, käyttävät samoja lupia kuin puhelin. Näin kello on helpompi ottaa käyttöön.\n\n Näihin lupiin saattaa kuulua pääsy kellon mikrofoniin ja sijaintiin."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml index 8d2b99e95696..773a34500e34 100644 --- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string> <string name="consent_back" msgid="2560683030046918882">"Retour"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transférer les autorisations de l\'application à votre montre"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Pour faciliter la configuration de votre montre, les applications installées sur celle-ci reprennent les mêmes autorisations que celles installées sur votre téléphone.\n\n Ces autorisations peuvent comprendre l\'accès au microphone et à la position de votre montre."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml index e6167cd8e2fb..73781b96f295 100644 --- a/packages/CompanionDeviceManager/res/values-fr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string> <string name="consent_back" msgid="2560683030046918882">"Retour"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transférer les autorisations de l\'appli vers la montre"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Pour que votre montre soit plus facile à configurer, les applis qui y sont installées pendant la configuration utiliseront les mêmes autorisations que votre téléphone.\n\n Il peut s\'agir, par exemple, de l\'accès au micro et à la position de votre montre."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index 3a948630ea34..1c65e11cea30 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string> <string name="consent_back" msgid="2560683030046918882">"Atrás"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferir os permisos de aplicacións ao reloxo"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Para que che resulte máis doado configurar o reloxo, as aplicacións que instales nel durante a configuración usarán os mesmos permisos que o teléfono.\n\n Entre estes permisos poden estar incluídos os de acceso ao micrófono e á localización do teléfono."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml index 6012c6c91a9f..879dfd130694 100644 --- a/packages/CompanionDeviceManager/res/values-gu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string> <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string> <string name="consent_back" msgid="2560683030046918882">"પાછળ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"તમારી ઘડિયાળમાં ઍપ પરવાનગીઓ ટ્રાન્સફર કરો"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"તમારી ઘડિયાળનું સેટઅપ કરવાનું સરળ બનાવવા માટે, સેટઅપ દરમિયાન તમારી ઘડિયાળ પર ઇન્સ્ટૉલ કરેલી ઍપ દ્વારા તમારા ફોન પર મળેલી પરવાનગીઓનો ઉપયોગ કરવામાં આવશે.\n\n આ પરવાનગીઓમાં તમારી ઘડિયાળના માઇક્રોફોન અને સ્થાન સંબંધિત માહિતીનો ઍક્સેસ શામેલ હોઈ શકે છે."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index aa0628ebb734..7e071f3d3291 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string> <string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string> <string name="consent_back" msgid="2560683030046918882">"वापस जाएं"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ऐप्लिकेशन से जुड़ी अनुमतियों को अपनी वॉच में ट्रांसफ़र करें"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"वॉच को सेट अप करने की प्रोसेस को आसान बनाने के लिए, उस पर इंस्टॉल किए गए ऐप्लिकेशन को भी वही अनुमतियां मिलेंगी जो आपने उन ऐप्लिकेशन को फ़ोन पर दी होंगी.\n\n इन अनुमतियों में, आपकी वॉच के माइक्रोफ़ोन और जगह की जानकारी का ऐक्सेस शामिल हो सकता है."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml index f5cd4281671c..fda9cfaf5dcf 100644 --- a/packages/CompanionDeviceManager/res/values-hr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string> <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string> <string name="consent_back" msgid="2560683030046918882">"Natrag"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Prijenos dopuštenja aplikacije na sat"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Kako bi postavljanje sata bilo jednostavnije, aplikacije instalirane na satu će tijekom postavljanja upotrebljavati ista dopuštenja kao telefon.\n\n Ta dopuštenja mogu uključivati pristup mikrofonu i lokaciji sata."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Davanje jednakih dopuštenja aplikacijama na uređaju <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> kao i na uređaju <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>To može uključivati pristup mikrofonu, kameri i lokaciji i druga dopuštenja za osjetljive podatke na uređaju <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>Ta dopuštenja uvijek možete promijeniti u postavkama na uređaju <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml index 1ea2de1ad792..1d8a39992fcb 100644 --- a/packages/CompanionDeviceManager/res/values-hu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string> <string name="consent_no" msgid="2640796915611404382">"Tiltás"</string> <string name="consent_back" msgid="2560683030046918882">"Vissza"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Alkalmazásengedélyek átvitele az órára"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Az óra beállításának megkönnyítése érdekében a beállítás során az órára telepített alkalmazások ugyanazokat az engedélyeket használják majd, mint a telefonja.\n\n Ezek az engedélyek magukban foglalhatják az óra mikrofonjához és helyadataihoz való hozzáférést."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml index 592a6c33624f..caa1c245e717 100644 --- a/packages/CompanionDeviceManager/res/values-hy/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string> <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string> <string name="consent_back" msgid="2560683030046918882">"Հետ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Հավելվածների թույլտվությունների տեղափոխում ժամացույց"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Կարգավորման ժամանակ ժամացույցում տեղադրված հավելվածների համար կօգտագործվեն նույն թույլտվությունները, ինչ հեռախոսում։\n\n Այդ թույլտվությունները կարող են ներառել ժամացույցի խոսափողի կամ տեղադրության տվյալների օգտագործումը։"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml index 905f7c548f2c..c328a6b1c818 100644 --- a/packages/CompanionDeviceManager/res/values-in/strings.xml +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string> <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string> <string name="consent_back" msgid="2560683030046918882">"Kembali"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfer izin aplikasi ke smartwatch"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Untuk mempermudah penyiapan smartwatch, aplikasi yang diinstal di smartwatch selama penyiapan akan menggunakan izin yang sama dengan ponsel.\n\n Izin ini dapat meliputi akses ke mikrofon dan lokasi smartwatch."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml index b389184cf9ae..4425b113cbe6 100644 --- a/packages/CompanionDeviceManager/res/values-is/strings.xml +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string> <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string> <string name="consent_back" msgid="2560683030046918882">"Til baka"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Flytja heimildir forrita yfir í úrið"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Til að auðvelda uppsetningu úrsins munu forrit sem eru sett upp í úrinu við uppsetningu nota sömu heimildir og stilltar eru í símanum.\n\n Þessar heimildir kunna að fela í sér aðgang að hljóðnema og staðsetningu úrsins."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml index ccf7e5557a51..a3a5410abf83 100644 --- a/packages/CompanionDeviceManager/res/values-it/strings.xml +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string> <string name="consent_no" msgid="2640796915611404382">"Non consentire"</string> <string name="consent_back" msgid="2560683030046918882">"Indietro"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Trasferisci le autorizzazioni app all\'orologio"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Per facilitare la configurazione dell\'orologio, le app installate su quest\'ultimo durante la configurazione useranno le stesse autorizzazioni delle app sul telefono.\n\n Queste autorizzazioni potrebbero includere l\'accesso al microfono e alla posizione dell\'orologio."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml index 2f9c7e4f8bdb..cfdb66aea686 100644 --- a/packages/CompanionDeviceManager/res/values-iw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string> <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string> <string name="consent_back" msgid="2560683030046918882">"חזרה"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"העברת ההרשאות הניתנות לאפליקציות אל השעון שלך"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"כדי לפשט את הגדרת השעון, אפליקציות שמותקנות במהלך ההגדרה יקבלו את אותן הרשאות שניתנו בטלפון.\n\n ההרשאות האלה עשויות לכלול גישה למיקרופון ולמיקום של השעון."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml index 7c887ffc66ab..d81f4d2f51bc 100644 --- a/packages/CompanionDeviceManager/res/values-ja/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"許可"</string> <string name="consent_no" msgid="2640796915611404382">"許可しない"</string> <string name="consent_back" msgid="2560683030046918882">"戻る"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"スマートウォッチへのアプリの権限の移行"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"スマートウォッチのセットアップを簡単にするため、セットアップ時にスマートウォッチにインストールされたアプリに、スマートフォンと同じ権限が適用されます。\n\n これらの権限には、スマートウォッチのマイクや位置情報へのアクセス権も含まれることがあります。"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml index 5b56c33441ae..ef2e66b122fb 100644 --- a/packages/CompanionDeviceManager/res/values-ka/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string> <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string> <string name="consent_back" msgid="2560683030046918882">"უკან"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"აპის ნებართვების საათისთვის გადაცემა"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"საათის დაყენების გასამარტივებლად თქვენს საათში დაინსტალირებული აპები იმავე ნებართვებს გამოიყენებს, რასაც ტელეფონზე იყენებს.\n\n ეს ნებართვები, შესაძლოა, მოიცავდეს თქვენი საათის მიკროფონსა და მდებარეობაზე წვდომას."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml index 67d3e8e8786f..c1129bbbe246 100644 --- a/packages/CompanionDeviceManager/res/values-kk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string> <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string> <string name="consent_back" msgid="2560683030046918882">"Артқа"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Қолданба рұқсаттарын сағатқа ауыстыру"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Реттеу кезінде сағатқа орнатылған қолданбалар телефондағыдай рұқсаттарды пайдаланады. Осылайша сағат оңай реттеледі.\n\n Бұл рұқсаттар сағаттың микрофоны мен геодерегін пайдалануды қамтиды."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml index 6c9c87475f18..bf8168ffd1a4 100644 --- a/packages/CompanionDeviceManager/res/values-km/strings.xml +++ b/packages/CompanionDeviceManager/res/values-km/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string> <string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string> <string name="consent_back" msgid="2560683030046918882">"ថយក្រោយ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ផ្ទេរការអនុញ្ញាតកម្មវិធីទៅនាឡិការបស់អ្នក"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"ដើម្បីជួយឱ្យការរៀបចំនាឡិការបស់អ្នកកាន់តែងាយស្រួល កម្មវិធីដែលបានដំឡើងនៅលើនាឡិការបស់អ្នកអំឡុងពេលរៀបចំនឹងប្រើការអនុញ្ញាតដូចគ្នានឹងទូរសព្ទរបស់អ្នកដែរ។\n\n ការអនុញ្ញាតទាំងនេះអាចរួមបញ្ចូលសិទ្ធិចូលប្រើទីតាំង និងមីក្រូហ្វូនរបស់នាឡិកាអ្នក។"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml index 3c71300fad9c..e31176f59f3c 100644 --- a/packages/CompanionDeviceManager/res/values-kn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string> <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string> <string name="consent_back" msgid="2560683030046918882">"ಹಿಂದೆ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ಆ್ಯಪ್ ಅನುಮತಿಗಳನ್ನು ನಿಮ್ಮ ವಾಚ್ಗೆ ವರ್ಗಾವಣೆ ಮಾಡಿ"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"ನಿಮ್ಮ ವಾಚ್ ಸೆಟಪ್ ಮಾಡುವುದನ್ನು ಸುಲಭವಾಗಿಸಲು, ಸೆಟಪ್ನ ಸಮಯದಲ್ಲಿ ನಿಮ್ಮ ವಾಚ್ನಲ್ಲಿ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿದ ಆ್ಯಪ್ಗಳು, ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿನ ಅನುಮತಿಗಳನ್ನೇ ಬಳಸಿಕೊಳ್ಳುತ್ತವೆ.\n\n ಈ ಅನುಮತಿಗಳು ನಿಮ್ಮ ವಾಚ್ನ ಮೈಕ್ರೊಫೋನ್ ಮತ್ತು ಸ್ಥಳದ ಪ್ರವೇಶವನ್ನು ಒಳಗೊಳ್ಳಬಹುದು."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 8a3ad35ff309..af97339ddb7d 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"허용"</string> <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string> <string name="consent_back" msgid="2560683030046918882">"뒤"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"앱 권한을 시계로 이전"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"시계를 더 쉽게 설정하기 위해 설정하는 동안 시계에 설치된 앱에서 휴대전화와 동일한 권한을 사용합니다.\n\n 이러한 권한에는 시계의 마이크 및 위치 정보에 대한 액세스가 포함될 수 있습니다."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml index 51252975b45f..0311621d89aa 100644 --- a/packages/CompanionDeviceManager/res/values-ky/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string> <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string> <string name="consent_back" msgid="2560683030046918882">"Артка"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Колдонмонун уруксаттарын саатка өткөрүү"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Сааттын жөндөлүшүн жеңилдетүү үчүн жөндөө учурунда саатыңызга орнотулган колдонмолор телефонуңуздагы уруксаттарды колдонот.\n\n Мындай уруксаттарга саатыңыздын микрофонун же жайгашкан жерин колдонуу кириши мүмкүн."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"<strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> түзмөгүнө да <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong> түзмөгүнө берилген уруксаттар берилсинби?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>Бул уруксаттарга микрофонго, камерага жана жайгашкан жерге кирүү мүмкүнчүлүгү жана <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong> түзмөгүндөгү башка купуя маалыматты көрүүгө уруксаттар кириши мүмкүн.</p> <p>Бул уруксаттарды каалаган убакта <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong> түзмөгүндөгү Жөндөөлөрдөн өзгөртө аласыз.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml index 42400a0543e9..7c269fe8848e 100644 --- a/packages/CompanionDeviceManager/res/values-lo/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string> <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string> <string name="consent_back" msgid="2560683030046918882">"ກັບຄືນ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ໂອນຍ້າຍການອະນຸຍາດແອັບໄປຫາໂມງຂອງທ່ານ"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"ເພື່ອເຮັດໃຫ້ຕັ້ງຄ່າໂມງຂອງທ່ານໄດ້ງ່າຍຂຶ້ນ, ແອັບທີ່ຕິດຕັ້ງຢູ່ໂມງຂອງທ່ານໃນລະຫວ່າງການຕັ້ງຄ່າຈະໃຊ້ການອະນຸຍາດດຽວກັນກັບໂທລະສັບຂອງທ່ານ.\n\n ການອະນຸຍາດເຫຼົ່ານີ້ອາດຮວມສິດເຂົ້າເຖິງໄມໂຄຣໂຟນ ແລະ ສະຖານທີ່ຂອງທ່ານນຳ."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml index f79ea0767fd4..248791da6e5b 100644 --- a/packages/CompanionDeviceManager/res/values-lt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string> <string name="consent_no" msgid="2640796915611404382">"Neleisti"</string> <string name="consent_back" msgid="2560683030046918882">"Atgal"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Laikrodžio programų perkėlimo leidimai"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Kad būtų lengviau nustatyti laikrodį, jame atliekant sąranką įdiegtoms programoms bus naudojami tie patys leidimai kaip jūsų telefone.\n\n Šie leidimai gali apimti prieigą prie laikrodžio mikrofono ir vietovės."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Suteikti <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> esančioms programoms tuos pačius leidimus kaip <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong> esančioms programoms?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>Gali būti įtraukti prieigos prie mikrofono, kameros ir vietovės leidimai ir kiti leidimai pasiekti neskelbtiną informaciją <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong> įrenginyje.</p> <p>Šiuos leidimus galite bet kada pakeisti „Nustatymų“ skiltyje <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong> įrenginyje.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml index ce4f7ac9129a..34fc5f18bfef 100644 --- a/packages/CompanionDeviceManager/res/values-lv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string> <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string> <string name="consent_back" msgid="2560683030046918882">"Atpakaļ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Lietotņu atļauju pārsūtīšana uz pulksteni"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Lai atvieglotu pulksteņa iestatīšanu, iestatīšanas laikā pulkstenī instalētās lietotnes saņems tādas pašas atļaujas, kādas tām ir tālrunī.\n\n Tostarp lietotnes var saņemt atļauju piekļūt pulksteņa mikrofonam un atrašanās vietai."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml index 7ea24e727a2a..e0de7adc1209 100644 --- a/packages/CompanionDeviceManager/res/values-mk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Префрлете ги дозволите за апликациите на вашиот часовник"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"За полесно поставувањето на часовникот, апликациите инсталирани на часовникот при поставувањето ќе ги користат истите дозволи како на телефонот.\n\n Овие дозволи може да опфаќаат пристап до микрофонот и локацијата на часовникот."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml index 0de423ca4154..abeefd926a41 100644 --- a/packages/CompanionDeviceManager/res/values-ml/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string> <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string> <string name="consent_back" msgid="2560683030046918882">"മടങ്ങുക"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"നിങ്ങളുടെ വാച്ചിലേക്ക് ആപ്പ് അനുമതികൾ കൈമാറുക"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"നിങ്ങളുടെ വാച്ച് സജ്ജീകരിക്കുന്നത് എളുപ്പമാക്കാൻ, സജ്ജീകരിക്കുമ്പോൾ ഫോണിലുള്ള അതേ അനുമതികൾ നിങ്ങളുടെ വാച്ചിൽ ഇൻസ്റ്റാൾ ചെയ്തിട്ടുള്ള ആപ്പുകൾ ഉപയോഗിക്കും.\n\n ഈ അനുമതികളിൽ നിങ്ങളുടെ വാച്ചിന്റെ മൈക്രോഫോണിലേക്കും ലോക്കേഷനിലേക്കുമുള്ള ആക്സസ് ഉൾപ്പെട്ടേക്കാം."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml index f4dd0e1e3f21..58e138584fd4 100644 --- a/packages/CompanionDeviceManager/res/values-mn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string> <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string> <string name="consent_back" msgid="2560683030046918882">"Буцах"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Цагандаа аппын зөвшөөрлийг шилжүүлэх"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Таны цагийг тохируулахад илүү хялбар болгохын тулд тохируулгын үеэр таны цаган дээр суулгасан аппууд нь утастай тань ижил зөвшөөрлийг ашиглана.\n\n Эдгээр зөвшөөрөлд таны цагийн микрофон болон байршлын хандалт зэрэг багтаж магадгүй."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"<strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> дээрх аппуудад <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong> дээрхтэй адил зөвшөөрөл өгөх үү?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>Үүнд Микрофон, Камер болон Байршлын хандалт болон <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong> дээрх бусад эмзэг зөвшөөрөл багтаж болно.</p> <p>Та эдгээр зөвшөөрлийг <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong> дээрх Тохиргоо хэсэгтээ хүссэн үедээ өөрчлөх боломжтой.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml index 30e6820d9712..4838091a2dc1 100644 --- a/packages/CompanionDeviceManager/res/values-mr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string> <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string> <string name="consent_back" msgid="2560683030046918882">"मागे जा"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"अॅप परवानग्या तुमच्या वॉचवर ट्रान्सफर करा"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"तुमचे वॉच सेट करणे आणखी सोपे करण्यासाठी, सेटअपदरम्यान तुमच्या वॉचवर इंस्टॉल केलेली ॲप्स ही तुमच्या फोनप्रमाणेच परवानग्या वापरतील.\n\n या परवानग्यांमध्ये तुमच्या वॉचचा मायक्रोफोन आणि स्थानाच्या अॅक्सेसचा समावेश असू शकतो."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml index a0b561a5ec27..4ad02ebacd39 100644 --- a/packages/CompanionDeviceManager/res/values-ms/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string> <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string> <string name="consent_back" msgid="2560683030046918882">"Kembali"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Pindahkan kebenaran apl pada jam tangan anda"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Untuk memudahkan penyediaan jam tangan anda, apl yang dipasang pada jam tangan anda semasa persediaan akan menggunakan kebenaran yang sama seperti telefon anda.\n\n Kebenaran ini mungkin termasuk akses kepada mikrofon dan lokasi jam tangan anda."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml index 31663e34d927..4710303d077f 100644 --- a/packages/CompanionDeviceManager/res/values-my/strings.xml +++ b/packages/CompanionDeviceManager/res/values-my/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string> <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string> <string name="consent_back" msgid="2560683030046918882">"နောက်သို့"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"သင်၏နာရီသို့ အက်ပ်ခွင့်ပြုချက်များ လွှဲပြောင်းရန်"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"သင်၏နာရီ စနစ်ထည့်သွင်းရာတွင် ပိုလွယ်ကူစေရန် စနစ်ထည့်သွင်းနေစဉ်အတွင်း နာရီတွင်ထည့်သွင်းသော အက်ပ်များသည် သင့်ဖုန်းနှင့် အလားတူခွင့်ပြုချက်များကို သုံးပါမည်။\n\n ဤခွင့်ပြုချက်များတွင် သင့်နာရီ၏ မိုက်ခရိုဖုန်းနှင့် တည်နေရာတို့ကို သုံးခွင့် ပါဝင်နိုင်သည်။"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml index 3f4db79064f3..5333477c6c5b 100644 --- a/packages/CompanionDeviceManager/res/values-nb/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string> <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string> <string name="consent_back" msgid="2560683030046918882">"Tilbake"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Overfør apptillatelser til klokken din"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"For å gjøre det enklere å konfigurere klokken din bruker apper som installeres på klokken under konfigureringen, samme tillatelser som på telefonen.\n\n Disse tillatelsene kan inkludere tilgang til mikrofonen på klokken og posisjon."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml index b13741e4340c..6336ee467958 100644 --- a/packages/CompanionDeviceManager/res/values-ne/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string> <string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string> <string name="consent_back" msgid="2560683030046918882">"पछाडि"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"एपलाई दिइएका अनुमति घडीमा ट्रान्स्फर गर्नुहोस्"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"तपाईंको घडी सेटअप गर्ने कार्य सजिलो बनाउनका लागि सेटअप गर्ने क्रममा तपाईंको घडीमा इन्स्टल गरिएका एपहरूले पनि तपाईंको फोनमा दिइएको जस्तै अनुमति प्रयोग गर्ने छन्।\n\n यी अनुमतिमा तपाईंको घडीको माइक्रोफोन र लोकेसन प्रयोग गर्ने जस्ता अनुमति पर्न सक्छन्।"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml index 4001c811d9a9..367800fd10bb 100644 --- a/packages/CompanionDeviceManager/res/values-nl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string> <string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string> <string name="consent_back" msgid="2560683030046918882">"Terug"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"App-rechten overzetten naar je horloge"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"We willen het makkelijker voor je maken om je horloge in te stellen. Daarom gebruiken apps die tijdens het instellen worden geïnstalleerd op je horloge, dezelfde rechten als op je telefoon.\n\n Deze rechten kunnen toegang tot de microfoon en locatie van je horloge omvatten."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml index 888f6e3864a9..36b55aa3ecf6 100644 --- a/packages/CompanionDeviceManager/res/values-or/strings.xml +++ b/packages/CompanionDeviceManager/res/values-or/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string> <string name="consent_back" msgid="2560683030046918882">"ପଛକୁ ଫେରନ୍ତୁ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ଆପଣଙ୍କ ୱାଚକୁ ଆପ ଅନୁମତିଗୁଡ଼ିକ ଟ୍ରାନ୍ସଫର କରନ୍ତୁ"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"ଆପଣଙ୍କ ୱାଚ ସେଟ ଅପ କରିବାକୁ ସହଜ କରିବା ପାଇଁ, ସେଟଅପ ସମୟରେ ଆପଣଙ୍କର ୱାଚରେ ଇନଷ୍ଟଲ କରାଯାଇଥିବା ଆପଗୁଡ଼ିକ ଆପଣଙ୍କ ଫୋନରେ ଥିବା ଆପଗୁଡ଼ିକ ପରି ସମାନ ଅନୁମତିଗୁଡ଼ିକ ବ୍ୟବହାର କରିବ।\n\n ଏହି ଅନୁମତିଗୁଡ଼ିକରେ ଆପଣଙ୍କ ୱାଚର ମାଇକ୍ରୋଫୋନ ଏବଂ ଲୋକେସନକୁ ଆକ୍ସେସ ଅନ୍ତର୍ଭୁକ୍ତ ହୋଇପାରେ।"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml index 8f16c54ec1cd..bffb7c377cfd 100644 --- a/packages/CompanionDeviceManager/res/values-pa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"ਇਜਾਜ਼ਤ ਦਿਓ"</string> <string name="consent_no" msgid="2640796915611404382">"ਇਜਾਜ਼ਤ ਨਾ ਦਿਓ"</string> <string name="consent_back" msgid="2560683030046918882">"ਪਿੱਛੇ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ਐਪ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਆਪਣੀ ਘੜੀ \'ਤੇ ਟ੍ਰਾਂਸਫ਼ਰ ਕਰੋ"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"ਤੁਹਾਡੀ ਘੜੀ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨਾ ਆਸਾਨ ਬਣਾਉਣ ਲਈ, ਤੁਹਾਡੀ ਘੜੀ \'ਤੇ ਸਥਾਪਤ ਐਪਾਂ ਸੈੱਟਅੱਪ ਦੌਰਾਨ ਉਹੀ ਇਜਾਜ਼ਤਾਂ ਵਰਤਣਗੀਆਂ ਜੋ ਤੁਹਾਡਾ ਫ਼ੋਨ ਵਰਤਦਾ ਹੈ।\n\n ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਵਿੱਚ ਤੁਹਾਡੀ ਘੜੀ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਅਤੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀ ਹੈ।"</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"ਕੀ <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> \'ਤੇ ਮੌਜੂਦ ਐਪਾਂ ਨੂੰ <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong> \'ਤੇ ਮੌਜੂਦ ਐਪਾਂ ਵਾਂਗ ਇਜਾਜ਼ਤਾਂ ਦੇਣੀਆਂ ਹਨ?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>ਇਸ ਵਿੱਚ ਮਾਈਕ੍ਰੋਫ਼ੋਨ, ਕੈਮਰਾ, ਟਿਕਾਣਾ ਪਹੁੰਚ ਅਤੇ <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong> \'ਤੇ ਮੌਜੂਦ ਹੋਰ ਸੰਵੇਦਨਸ਼ੀਲ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਸੰਬੰਧੀ ਇਜਾਜ਼ਤਾਂ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀਆਂ ਹਨ।</p> <p>ਤੁਸੀਂ <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong> \'ਤੇ ਮੌਜੂਦ ਆਪਣੀਆਂ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਕਦੇ ਵੀ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml index 840323854852..f84746a1bd3d 100644 --- a/packages/CompanionDeviceManager/res/values-pl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string> <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string> <string name="consent_back" msgid="2560683030046918882">"Wstecz"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Przenieś uprawnienia aplikacji na zegarek"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Aby łatwiej było skonfigurować zegarek, aplikacje zainstalowane na nim podczas konfiguracji będą korzystały z tych samych uprawnień co telefon.\n\n Może to oznaczać dostęp do mikrofonu i lokalizacji na zegarku."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml index 7f081f0c9f4d..c87eacdbf4f1 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> <string name="consent_back" msgid="2560683030046918882">"Voltar"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferir as permissões de apps para o relógio"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Para facilitar a configuração do relógio, os apps instalados nele durante a configuração vão usar as mesmas permissões que o smartphone.\n\n Essas permissões podem incluir acesso ao microfone ou à localização do relógio."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml index 6b0ac35c9ff7..219d32aab7a5 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> <string name="consent_back" msgid="2560683030046918882">"Voltar"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfira as autorizações da app para o seu relógio"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Para facilitar a configuração do seu relógio, as apps instaladas no mesmo durante a configuração utilizarão as mesmas autorizações que o telemóvel.\n\n Estas autorizações podem incluir o acesso ao microfone e à localização do seu relógio."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml index 7f081f0c9f4d..c87eacdbf4f1 100644 --- a/packages/CompanionDeviceManager/res/values-pt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> <string name="consent_back" msgid="2560683030046918882">"Voltar"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferir as permissões de apps para o relógio"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Para facilitar a configuração do relógio, os apps instalados nele durante a configuração vão usar as mesmas permissões que o smartphone.\n\n Essas permissões podem incluir acesso ao microfone ou à localização do relógio."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml index 2cfbdbbc885e..73a9ab5cbb96 100644 --- a/packages/CompanionDeviceManager/res/values-ro/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string> <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string> <string name="consent_back" msgid="2560683030046918882">"Înapoi"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferați permisiunile pentru aplicații pe ceas"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Ca să configurați mai ușor ceasul, aplicațiile instalate pe ceas în timpul procesului de configurare vor folosi aceleași permisiuni ca telefonul.\n\n Între acestea se poate număra accesul la microfonul și locația ceasului."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml index 2b24fa212bf7..706f9f827f36 100644 --- a/packages/CompanionDeviceManager/res/values-ru/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string> <string name="consent_no" msgid="2640796915611404382">"Запретить"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Перенос разрешений для приложений на часы"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Для приложений, установленных на часы во время настройки, будут использоваться те же разрешения, что и на телефоне.\n\n Например, может быть включен доступ к микрофону на часах или сведениям о местоположении."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml index 94078a49db86..3180e42704ff 100644 --- a/packages/CompanionDeviceManager/res/values-si/strings.xml +++ b/packages/CompanionDeviceManager/res/values-si/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string> <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string> <string name="consent_back" msgid="2560683030046918882">"ආපසු"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ඔබගේ ඔරලෝසුවට යෙදුම් අවසර මාරු කිරීම"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"ඔබගේ ඔරලෝසුව පිහිටුවීම පහසු කිරීමට, පිහිටුවීමේදී ඔබගේ ඔරලෝසුවේ ස්ථාපනය කර ඇති යෙදුම් ඔබගේ දුරකථනයට සමාන අවසර භාවිත කරනු ඇත.\n\n මෙම අවසරවලට ඔබගේ ඔරලෝසුවේ මයික්රෆෝනයට සහ ස්ථානයට ප්රවේශය ඇතුළත් විය හැකිය."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index 9a5c87ea6ac9..0fbf4f469ddb 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string> <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string> <string name="consent_back" msgid="2560683030046918882">"Späť"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Presun povolení aplikácie do hodiniek"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"V rámci zjednodušenia nastavenia hodiniek budú aplikácie nainštalované do hodiniek pri nastavovaní používať rovnaké povolenia ako váš telefón.\n\n Tieto povolenia môžu zahrnovať prístup k mikrofónu a polohe hodiniek."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml index e28ba2f6ead7..49832fe6226d 100644 --- a/packages/CompanionDeviceManager/res/values-sl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string> <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string> <string name="consent_back" msgid="2560683030046918882">"Nazaj"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Prenos dovoljenj za aplikacije v uro"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Za lažjo nastavitev ure bodo aplikacije, ki so bile med nastavljanjem nameščene v uri, uporabljale enaka dovoljenja kot tiste v telefonu.\n\n Ta dovoljenja lahko vključujejo dostop do mikrofona in lokacije ure."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Ali želite aplikacijam v napravi <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> odobriti enaka dovoljenja kot v napravi <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>To lahko vključuje dostop do mikrofona, fotoaparata in lokacije ter druga občutljiva dovoljenja v napravi <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>Ta dovoljenja lahko kadar koli spremenite v nastavitvah v napravi <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml index ba13505e1438..ac89babc0dd1 100644 --- a/packages/CompanionDeviceManager/res/values-sq/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string> <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string> <string name="consent_back" msgid="2560683030046918882">"Pas"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transfero lejet e aplikacionit te ora jote"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Për ta bërë më të lehtë konfigurimin e orës, aplikacionet e instaluara në orën tënde gjatë konfigurimit do të përdorin të njëjtat leje si telefoni yt.\n\n Këto leje mund të përfshijnë qasje në mikrofonin dhe vendndodhjen e orës."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml index 3be67ff2837c..2ae7aee8bc76 100644 --- a/packages/CompanionDeviceManager/res/values-sr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Пренесите дозволе за апликације на сат"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Да бисмо поједноставили подешавање сата, апликације инсталиране на сату током подешавања ће користити исте дозволе као телефон.\n\n Те дозволе могу да обухватају приступ микрофону и локацији сата."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml index 19a245658727..234552dd0d6c 100644 --- a/packages/CompanionDeviceManager/res/values-sv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string> <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string> <string name="consent_back" msgid="2560683030046918882">"Tillbaka"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Överför appbehörigheter till klockan"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Appar som installeras på klockan under konfigureringen får samma behörigheter som de har på telefonen så att konfigureringen ska bli enklare.\n\n Behörigheterna kan omfatta åtkomst till klockans mikrofon och plats."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml index 68a15348b506..a5f0b54caeea 100644 --- a/packages/CompanionDeviceManager/res/values-sw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string> <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string> <string name="consent_back" msgid="2560683030046918882">"Nyuma"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Hamishia idhini za programu kwenye saa yako"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Ili kurahisisha kuweka mipangilio ya saa yako, programu ambazo zimesakinishwa kwenye saa yako wakati wa kuweka mipangilio zitatumia ruhusa sawa na zinazotumika kwenye simu yako.\n\n Ruhusa hizi huenda zikajumuisha ufikiaji wa maikrofoni ya saa yako na maelezo ya mahali ilipo saa yako."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml index c3c7eece58a3..0a1bc89d4e13 100644 --- a/packages/CompanionDeviceManager/res/values-ta/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string> <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string> <string name="consent_back" msgid="2560683030046918882">"பின்செல்"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"ஆப்ஸ் அனுமதிகளை உங்கள் வாட்ச்சிற்கு மாற்றுதல்"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"உங்கள் வாட்ச் அமைவை எளிதாக்க, உங்கள் மொபைலில் வழங்கியுள்ள அனுமதிகளையே அமைவின்போது வாட்ச்சில் நிறுவப்பட்ட ஆப்ஸும் பயன்படுத்தும்.\n\n உங்கள் வாட்ச்சிலுள்ள மைக்ரோஃபோன், இருப்பிடம் ஆகியவற்றுக்கான அணுகலும் இந்த அனுமதிகளில் அடங்கக்கூடும்."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml index ec3289dec687..6ce530b3392d 100644 --- a/packages/CompanionDeviceManager/res/values-te/strings.xml +++ b/packages/CompanionDeviceManager/res/values-te/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string> <string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string> <string name="consent_back" msgid="2560683030046918882">"వెనుకకు"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"మీ వాచ్కు యాప్ అనుమతులను బదిలీ చేయండి"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"మీ వాచ్ను సెటప్ చేయడాన్ని సులభతరం చేయడానికి, సెటప్ సమయంలో మీ వాచ్లో ఇన్స్టాల్ చేయబడిన యాప్లు మీ ఫోన్లో యాప్లకు ఉన్న అవే అనుమతులను ఉపయోగిస్తాయి.\n\n ఈ అనుమతులతో మీ వాచ్ మైక్రోఫోన్, అలాగే లొకేషన్ కూడా ఉండవచ్చు."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"<strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong>లోని యాప్లకు <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>లో ఉన్న అనుమతులను ఇవ్వాలా?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p><strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>లో మైక్రోఫోన్, కెమెరా, లొకేషన్ యాక్సెస్, ఇంకా ఇతర గోప్యమైన సమాచార యాక్సెస్ అనుమతులు ఇందులో ఉండవచ్చు.</p> <p>మీరు <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>లో మీ సెట్టింగ్లలో ఎప్పుడైనా ఈ అనుమతులను మార్చవచ్చు.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml index 2b4742612858..aa655a938fea 100644 --- a/packages/CompanionDeviceManager/res/values-th/strings.xml +++ b/packages/CompanionDeviceManager/res/values-th/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string> <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string> <string name="consent_back" msgid="2560683030046918882">"กลับ"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"โอนสิทธิ์ของแอปไปยังนาฬิกา"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"แอปที่ติดตั้งในนาฬิการะหว่างการตั้งค่าจะใช้สิทธิ์เดียวกันกับโทรศัพท์เพื่อให้การตั้งค่านาฬิกาง่ายขึ้น\n\n สิทธิ์เหล่านี้อาจรวมการเข้าถึงไมโครโฟนและตำแหน่งของนาฬิกา"</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"ให้แอปใน <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> มีสิทธิ์เหมือนกับใน <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong> ไหม"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>โดยอาจรวมถึงสิทธิ์เข้าถึงไมโครโฟน กล้อง และตำแหน่ง ตลอดจนสิทธิ์ที่มีความละเอียดอ่อนอื่นๆ ใน <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>คุณเปลี่ยนแปลงสิทธิ์เหล่านี้ได้ทุกเมื่อในการตั้งค่าใน <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml index 15953a6065c4..b9ce50b55d8d 100644 --- a/packages/CompanionDeviceManager/res/values-tl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string> <string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string> <string name="consent_back" msgid="2560683030046918882">"Bumalik"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Ilipat sa iyong relo ang mga pahintulot sa app"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Para gawing mas madali na i-set up ang iyong relo, gagamitin ng mga app na naka-install sa relo mo sa oras ng pag-set up ang mga pahintulot na ginagamit din sa iyong telepono.\n\n Posibleng kasama sa mga pahintulot na ito ang access sa mikropono at lokasyon ng iyong relo."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Bigyan ang mga app sa <strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> ng mga pahintulot na mayroon din sa <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>Posibleng kabilang dito ang access sa Mikropono, Camera, at Lokasyon, at iba pang pahintulot sa sensitibong impormasyon sa <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong>.</p> <p>Puwede mong baguhin ang mga pahintulot na ito anumang oras sa iyong Mga Setting sa <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong>.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml index 8e0938ba004a..487ecb354585 100644 --- a/packages/CompanionDeviceManager/res/values-tr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string> <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string> <string name="consent_back" msgid="2560683030046918882">"Geri"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Uygulama izinlerini saatinize aktarma"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Kurulum sırasında saatinize yüklenen uygulamalar, saat kurulumunuzu kolaylaştırmak için telefonunuzla aynı izinleri kullanır.\n\n Saatinizin mikrofonuna ve konumuna erişim bu izinlere dahil olabilir."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml index 3638058d2707..ba3d9b5a3ffa 100644 --- a/packages/CompanionDeviceManager/res/values-uk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string> <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Перенести дозволи для додатків на годинник"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Задля зручності додатки, установлені на годиннику протягом налаштування, використовуватимуть ті самі дозволи, що й на телефоні.\n\n До таких дозволів може належати доступ до мікрофона й геоданих годинника."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml index a9512097985d..fba5c45727bd 100644 --- a/packages/CompanionDeviceManager/res/values-ur/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string> <string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string> <string name="consent_back" msgid="2560683030046918882">"پیچھے"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"اپنی گھڑی پر ایپ کی اجازتیں منتقل کریں"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"آپ کی گھڑی کو سیٹ اپ کرنے کے عمل کو زیادہ آسان بنانے کے لیے، سیٹ اپ کے دوران آپ کی گھڑی پر انسٹال کردہ ایپس انہیں اجازتوں کا استعمال کریں گی جن کا استعمال آپ کا فون کرتا ہے۔\n\n ان اجازتوں میں آپ کی گھڑی کے مائیکروفون اور مقام تک کی رسائی شامل ہو سکتی ہے۔"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml index 455f9efce52f..f2311fe1a675 100644 --- a/packages/CompanionDeviceManager/res/values-uz/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml @@ -42,6 +42,6 @@ <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string> <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string> <string name="consent_back" msgid="2560683030046918882">"Orqaga"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Ilova uchun ruxsatlarni soatingizga uzating"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Soatingizni sozlashni qulaylashtirish maqsadida sozlash paytida soatingizga oʻrnatilgan ilovalar telefoningiz bilan bir xil ruxsatlardan foydalanadi.\n\n Bunday ruxsatlarga soatingiz mikrofoni va joylashuv axborotiga ruxsatlar kirishi mumkin."</string> + <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"<strong><xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g></strong> ilovalariga <strong><xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g></strong> qurilmasidagi kabi bir xil ruxsatlar berilsinmi?"</string> + <string name="permission_sync_summary" msgid="4866838188678457084">"<p>Bunga <strong><xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g></strong> qurilmasidagi Mikrofon, Kamera, Joylashuv kabi muhim ruxsatlar kirishi mumkin.</p> <p>Bu ruxsatlarni istalgan vaqt <strong><xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g></strong> Sozlamalari orqali oʻzgartirish mumkin.</p>"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml index 0cc362b8d49e..28b66adedf36 100644 --- a/packages/CompanionDeviceManager/res/values-vi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string> <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string> <string name="consent_back" msgid="2560683030046918882">"Quay lại"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Chuyển quyền cho ứng dụng sang đồng hồ"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Để thiết lập đồng hồ dễ dàng hơn, trong quá trình thiết lập, các ứng dụng được cài đặt trên đồng hồ của bạn sẽ sử dụng các quyền giống như trên điện thoại.\n\n Các quyền này có thể bao gồm quyền sử dụng micrô và thông tin vị trí của đồng hồ."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml index 5286d75eb983..21db274d969d 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"允许"</string> <string name="consent_no" msgid="2640796915611404382">"不允许"</string> <string name="consent_back" msgid="2560683030046918882">"返回"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"将应用权限转让给手表"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"为了让您更轻松地设置手表,在设置过程中安装在手表上的应用将使用与手机相同的权限。\n\n这些权限可能包括使用手表的麦克风和位置信息。"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml index 1e6f515e17f5..9dd03ea1329f 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"允許"</string> <string name="consent_no" msgid="2640796915611404382">"不允許"</string> <string name="consent_back" msgid="2560683030046918882">"返回"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"將應用程式權限轉移至手錶"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"為簡化手錶的設定程序,在設定過程中安裝到手錶上的應用程式都將沿用手機上的權限。\n\n這些權限可能包括手錶麥克風和位置的存取權。"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml index 9cb91d0f1ef7..0b6fa4479c29 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"允許"</string> <string name="consent_no" msgid="2640796915611404382">"不允許"</string> <string name="consent_back" msgid="2560683030046918882">"返回"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"將應用程式權限轉移到手錶上"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"為簡化手錶的設定程序,只要是在設定過程中安裝到手錶上的應用程式,都將沿用手機上的權限。\n\n 這些權限可能包括手錶的麥克風和位置資訊存取權。"</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml index a86eec2b085d..38622a41efa9 100644 --- a/packages/CompanionDeviceManager/res/values-zu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml @@ -42,6 +42,8 @@ <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string> <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string> <string name="consent_back" msgid="2560683030046918882">"Emuva"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Dlulisela izimvume ze-app ewashini lakho"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Ukuze wenze kube lula ukusetha iwashi lakho, ama-app afakwe ewashini lakho phakathi nokusetha azosebenzisa izimvume ezifanayo nezefoni yakho.\n\n Lezi zimvume zingabandakanya ukufinyelela kumakrofoni nendawo yewashi lakho."</string> + <!-- no translation found for permission_sync_confirmation_title (4409622174437248702) --> + <skip /> + <!-- no translation found for permission_sync_summary (4866838188678457084) --> + <skip /> </resources> diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 586a0223b460..fca9ab9a42e8 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -107,13 +107,11 @@ <string name="consent_back">Back</string> <!-- ================== System data transfer ==================== --> - <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=60] --> - <string name="permission_sync_confirmation_title">Transfer app permissions to your - watch</string> - - <!-- Text of the permission sync explanation in the confirmation dialog. [CHAR LIMIT=400] --> - <string name="permission_sync_summary">To make it easier to set up your watch, - apps installed on your watch during setup will use the same permissions as your phone.\n\n - These permissions may include access to your watch\u2019s microphone and location.</string> + <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=NONE] --> + <string name="permission_sync_confirmation_title">Give apps on <strong><xliff:g id="companion_device_name" example="Galaxy Watch 5">%1$s</xliff:g></strong> the same permissions as on <strong><xliff:g id="primary_device_name" example="Pixel 6">%2$s</xliff:g></strong>?</string> + + <!-- Text of the permission sync explanation in the confirmation dialog. [CHAR LIMIT=NONE] --> + <string name="permission_sync_summary"><p>This may include Microphone, Camera, and Location access, and other sensitive permissions on <strong><xliff:g id="companion_device_name" example="Galaxy Watch 5">%1$s</xliff:g></strong>.</p> + <p>You can change these permissions any time in your Settings on <strong><xliff:g id="companion_device_name" example="Galaxy Watch 5">%1$s</xliff:g></strong>.</p></string> </resources> diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml index c38323f5f73c..809e98e13b60 100644 --- a/packages/CompanionDeviceManager/res/values/styles.xml +++ b/packages/CompanionDeviceManager/res/values/styles.xml @@ -49,7 +49,6 @@ <style name="DescriptionSummary"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> - <item name="android:gravity">center</item> <item name="android:layout_marginTop">18dp</item> <item name="android:layout_marginLeft">18dp</item> <item name="android:layout_marginRight">18dp</item> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 37cbf3017f0b..e71c945f6327 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -307,6 +307,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements private void onUserSelectedDevice(@NonNull DeviceFilterPair<?> selectedDevice) { final MacAddress macAddress = selectedDevice.getMacAddress(); + mRequest.setDisplayName(selectedDevice.getDisplayName()); onAssociationApproved(macAddress); } @@ -486,6 +487,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements mSelectedDevice = requireNonNull(deviceFilterPairs.get(0)); final String deviceName = mSelectedDevice.getDisplayName(); + mRequest.setDisplayName(deviceName); final Spanned title = getHtmlFromResources( this, R.string.confirmation_title, appLabel, deviceName); final Spanned summary; diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java index 67efa03b645f..93040b58d74b 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java @@ -16,20 +16,22 @@ package com.android.companiondevicemanager; +import static android.companion.datatransfer.SystemDataTransferRequest.DATA_TYPE_PERMISSION_SYNC; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; +import static com.android.companiondevicemanager.Utils.getHtmlFromResources; + import static java.util.Objects.requireNonNull; import android.app.Activity; -import android.companion.SystemDataTransferRequest; +import android.companion.datatransfer.PermissionSyncRequest; +import android.companion.datatransfer.SystemDataTransferRequest; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.os.ResultReceiver; -import android.text.Html; import android.util.Log; -import android.view.View; import android.widget.Button; -import android.widget.ListView; import android.widget.TextView; /** @@ -39,14 +41,18 @@ public class CompanionDeviceDataTransferActivity extends Activity { private static final String LOG_TAG = CompanionDeviceDataTransferActivity.class.getSimpleName(); - // UI -> SystemDataTransferProcessor - private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0; - private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1; - private static final String EXTRA_SYSTEM_DATA_TRANSFER_REQUEST = "system_data_transfer_request"; + // Intent data keys from SystemDataTransferProcessor + private static final String EXTRA_PERMISSION_SYNC_REQUEST = "permission_sync_request"; + private static final String EXTRA_COMPANION_DEVICE_NAME = "companion_device_name"; private static final String EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER = "system_data_transfer_result_receiver"; + // Intent data keys to SystemDataTransferProcessor + private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0; + private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1; + private SystemDataTransferRequest mRequest; + private CharSequence mCompanionDeviceName; private ResultReceiver mCdmServiceReceiver; @Override @@ -61,23 +67,27 @@ public class CompanionDeviceDataTransferActivity extends Activity { TextView titleView = findViewById(R.id.title); TextView summaryView = findViewById(R.id.summary); - ListView listView = findViewById(R.id.device_list); - listView.setVisibility(View.GONE); Button allowButton = findViewById(R.id.btn_positive); Button disallowButton = findViewById(R.id.btn_negative); final Intent intent = getIntent(); - mRequest = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST); - mCdmServiceReceiver = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER); + mRequest = intent.getParcelableExtra(EXTRA_PERMISSION_SYNC_REQUEST, + PermissionSyncRequest.class); + mCompanionDeviceName = intent.getCharSequenceExtra(EXTRA_COMPANION_DEVICE_NAME); + mCdmServiceReceiver = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER, + ResultReceiver.class); requireNonNull(mRequest); requireNonNull(mCdmServiceReceiver); - if (mRequest.isPermissionSyncAllPackages() - || !mRequest.getPermissionSyncPackages().isEmpty()) { - titleView.setText(Html.fromHtml(getString( - R.string.permission_sync_confirmation_title), 0)); - summaryView.setText(getString(R.string.permission_sync_summary)); + final String primaryDeviceName = Build.MODEL; + + if (mRequest.getDataType() == DATA_TYPE_PERMISSION_SYNC) { + titleView.setText(getHtmlFromResources(this, + R.string.permission_sync_confirmation_title, mCompanionDeviceName, + primaryDeviceName)); + summaryView.setText(getHtmlFromResources(this, R.string.permission_sync_summary, + mCompanionDeviceName)); allowButton.setOnClickListener(v -> allow()); disallowButton.setOnClickListener(v -> disallow()); } @@ -101,7 +111,9 @@ public class CompanionDeviceDataTransferActivity extends Activity { private void sendDataToReceiver(int cdmResultCode) { Bundle data = new Bundle(); - data.putParcelable(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST, mRequest); + if (mRequest instanceof PermissionSyncRequest) { + data.putParcelable(EXTRA_PERMISSION_SYNC_REQUEST, (PermissionSyncRequest) mRequest); + } mCdmServiceReceiver.send(cdmResultCode, data); } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 53309fe3464a..7e835c89a821 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Stel terug"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwyder"</string> <string name="guest_resetting" msgid="7822120170191509566">"Stel tans gassessie terug …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Stel gastesessie terug?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Dit sal ’n nuwe gastesessie begin en alle programme en data van die huidige sessie uitvee"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Verlaat gasmodus?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Dit sal programme en data in die huidige gastesessie uitvee"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Gaan uit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Stoor gasaktiwiteit?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Jy kan aktiwiteit in die huidige sessie stoor of alle programme en data uitvee"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Vee uit"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Stoor"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Verlaat gasmodus"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Stel jou gastesessie terug"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Verlaat gasmodus"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Alle aktiwiteit sal uitgevee word wanneer jy uitgaan"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Jy kan jou aktiwiteit stoor of uitvee wanneer jy uitgaan"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Stel terug om sessie-aktiwiteit nou uit te vee, of jy kan aktiwiteit stoor of uitvee wanneer jy uitgaan"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index d24bbae4cc1a..3ec8d736ae4d 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ዳግም አስጀምር"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"አስወግድ"</string> <string name="guest_resetting" msgid="7822120170191509566">"እንግዳን ዳግም በማስጀመር ላይ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"የእንግዳ ክፍለ-ጊዜ ዳግም ይጀመር?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ይህ አዲስ የእንግዳ ክፍለ-ጊዜ ይጀምራል እና ሁሉንም መተግበሪያዎች እና ውሂብ አሁን ካለው ክፍለ-ጊዜ ይሰርዛል"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ከእንግዳ ሁኔታ ይውጣ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ይህ አሁን ካለው የእንግዳ ክፍለ-ጊዜ መተግበሪያዎችን እና ውሂብን ይሰርዛል"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ውጣ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"የእንግዳ እንቅስቃሴ ይቀመጥ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"እንቅስቃሴን አሁን ካለው ክፍለ-ጊዜ ማስቀመጥ ወይም ሁሉንም መተግበሪያዎች እና ውሂብ መሰረዝ ይችላሉ"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ሰርዝ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"አስቀምጥ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ከእንግዳ ሁኔታ ውጣ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"የእንግዳ ክፍለ-ጊዜን ዳግም አስጀምር"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"እንግዳ ያስወጡ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"በሚወጡበት ጊዜ ሁሉም እንቅስቃሴዎች ይሰረዛሉ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"በሚወጡበት ጊዜ እንቅስቃሴዎን ማስቀመጥ ወይም መሰረዝ ይችላሉ"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"የክፍለ-ጊዜ እንቅስቃሴን አሁን ለመሰረዝ ዳግም ያስጀምሩ፣ ወይም በሚወጡበት ጊዜ እንቅስቃሴን ማስቀመጥ ወይም መሰረዝ ይችላሉ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 6bcac9deead2..bdf438ba5ef1 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"إعادة الضبط"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"إزالة"</string> <string name="guest_resetting" msgid="7822120170191509566">"جارٍ إعادة ضبط جلسة الضيف…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index b3bba95f949b..66c6f20fac36 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ৰিছেট কৰক"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"আঁতৰাওক"</string> <string name="guest_resetting" msgid="7822120170191509566">"অতিথিৰ ছেশ্বন ৰিছেট কৰি থকা হৈছে…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"অতিথিৰ ছেশ্বন ৰিছেট কৰিবনে?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"এইটোৱে এটা অতিথিৰ ছেশ্বন আৰম্ভ কৰিব আৰু বৰ্তমানৰ ছেশ্বনটোৰ পৰা আটাইবোৰ এপ্ আৰু ডেটা মচিব"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"অতিথি ম’ডৰ পৰা বাহিৰ হ’বনে?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"এইটোৱে বৰ্তমানৰ অতিথিৰ ছেশ্বনটোৰ পৰা এপ্ আৰু ডেটা মচিব"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"বাহিৰ হওক"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"অতিথিৰ কাৰ্যকলাপ ছেভ কৰিবনে?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"আপুনি বৰ্তমানৰ ছেশ্বনটোৰ পৰা কাৰ্যকলাপ ছেভ কৰিব পাৰে অথবা আটাইবোৰ এপ্ আৰু ডেটা মচিব পাৰে"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"মচক"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ছেভ কৰক"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"অতিথি ম’ডৰ পৰা বাহিৰ হওক"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"অতিথিৰ ছেশ্বন ৰিছেট কৰক"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"অতিথিৰ ছেশ্বনৰ পৰা বাহিৰ হওক"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"বাহিৰ হওঁতে আটাইবোৰ কাৰ্যকলাপ মচা হ’ব"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"আপুনি বাহিৰ হওঁতে নিজৰ কাৰ্যকলাপ ছেভ কৰিব অথবা মচিব পাৰে"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"এতিয়াই ছেশ্বনৰ কাৰ্যকলাপ ৰিছেট কৰক অথবা মচক অথবা আপুনি বাহিৰ হওঁতে কাৰ্যকলাপ ছেভ কৰিব অথবা মচিব পাৰে"</string> <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ফট’ বাছনি কৰক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index ca78c68c83df..ed7c4b4cd847 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırlayın"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Silin"</string> <string name="guest_resetting" msgid="7822120170191509566">"Qonaq məlumatı sıfırlanır…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Qonaq sessiyası sıfırlansın?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bu, yeni qonaq sessiyası başladacaq və cari sessiyadan bütün tətbiqləri və datanı siləcək"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Qonaq rejimindən çıxılsın?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bununla cari qonaq sessiyasındakı bütün tətbiqlər və data silinəcək"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Çıxın"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Qonaq fəaliyyəti saxlansın?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Cari sessiyadakı fəaliyyəti saxlaya və ya bütün tətbiq və datanı silə bilərsiniz"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Silin"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Yadda saxlayın"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Qonaq rejimindən çıxın"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Qonaq sessiyasını sıfırlayın"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Qonaq rejimindən çıxın"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Çıxış zamanı bütün fəaliyyətlər silinəcək"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Çıxışda fəaliyyətinizi saxlaya və ya silə bilərsiniz"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Sessiya fəaliyyətini indi silmək üçün sıfırlayın və ya çıxışda fəaliyyəti saxlaya və ya silə bilərsiniz"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Foto seçin"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index fb7e21d78caa..2ab12efb8398 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Sesija gosta se resetuje…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite da resetujete sesiju gosta?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Time ćete pokrenuti novu sesiju gosta i izbrisati sve aplikacije i podatke iz aktuelne sesije"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Izaći ćete iz režima gosta?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time ćete izbrisati sve aplikacije i podatke iz aktuelne sesije gosta"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izađi"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Sačuvaćete aktivnosti gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Sačuvajte aktivnosti iz aktuelne sesije ili izbrišite sve aplikacije i podatke"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Sačuvaj"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Izađi iz režima gosta"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Resetuj sesiju gosta"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izađi iz režima gosta"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve aktivnosti će biti izbrisane pri izlazu"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Možete da sačuvate ili izbrišete aktivnosti pri izlazu"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetujete da biste izbrisali aktivnosti sesije odmah ili možete da sačuvate ili izbrišete aktivnosti pri izlazu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Izaberite sliku"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index da9caf604a7d..ca25ed9d7458 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скінуць"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Выдаліць"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ідзе скід гасцявога сеанса…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 12a421dcd1e1..00f76bc5f388 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Нулиране"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Премахване"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сесията като гост се нулира…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 4b03267c87ba..9f0af5d0bc40 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"রিসেট করুন"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"সরান"</string> <string name="guest_resetting" msgid="7822120170191509566">"গেস্ট সেশন রিসেট করা হচ্ছে..."</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index fae2cd662251..77812760f43c 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Poništavanje sesije gosta…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite li poništiti gostujuću sesiju?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Time će se pokrenuti nova gostujuća sesija i izbrisati sve aplikacije i podaci iz trenutačne sesije."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Napustiti način rada za goste?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time će se izbrisati aplikacije i podaci iz trenutačne gostujuće sesije."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izlaz"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Spremiti aktivnosti gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete spremiti aktivnosti iz ove sesije ili izbrisati sve aplikacije i podatke"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Spremi"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Izlaz iz načina rada za goste"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Poništi gostujuću sesiju"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izlaz iz gostujuće sesije"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve će se aktivnosti izbrisati na izlasku"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Možete spremiti ili izbrisati svoje aktivnosti na izlasku"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Poništite da biste odmah izbrisali aktivnost sesije, a možete i spremiti ili izbrisati aktivnost na izlasku."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index e3dca33ed933..83c799b31f1c 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restableix"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Suprimeix"</string> <string name="guest_resetting" msgid="7822120170191509566">"S\'està restablint el convidat…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 58c31eb6a0fd..157e3fc9fa5e 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovat"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstranit"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetování hosta…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index c982aa76e38d..17327af023e0 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nulstil"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nulstiller gæst…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 68d025602b76..be5964bfbac6 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Zurücksetzen"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Entfernen"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gast wird zurückgesetzt…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Foto auswählen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 44fd24fd0287..25334b68aa98 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Επαναφορά"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Κατάργηση"</string> <string name="guest_resetting" msgid="7822120170191509566">"Επαναφορά επισκέπτη…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 74e2dbbd05c9..23121e244477 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index eacaad02883f..5c5b6b9ddc3c 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 74e2dbbd05c9..23121e244477 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 74e2dbbd05c9..23121e244477 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 70227dce71e4..df280cdbbf8a 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 20baf883cd90..0ac9fe1877f4 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 9df8e7e6634c..d3baf2c67df4 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"¿Restablecer sesión de invitado?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Se iniciará una nueva sesión de invitado y se borrarán todas las aplicaciones y datos de esta sesión"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"¿Salir del modo invitados?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se eliminarán todas las aplicaciones y datos de la sesión de invitado"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Salir"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"¿Guardar actividad de invitados?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puedes guardar la actividad de esta sesión o eliminar las aplicaciones y datos"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eliminar"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Guardar"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Salir del modo invitados"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Restablecer sesión de invitado"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Salir del modo invitados"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toda la actividad se eliminará cuando salgas"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puedes guardar o eliminar tu actividad cuando salgas"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restablece la sesión para eliminar la actividad ahora, o guarda o elimina la actividad cuando salgas"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 6cc50575ab44..949ef176d1d7 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Lähtesta"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eemalda"</string> <string name="guest_resetting" msgid="7822120170191509566">"Külastajaseansi lähtestamine …"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 1154f9c31d4c..daabcdad077b 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Berrezarri"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kendu"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gonbidatuentzako saioa berrezartzen…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 95ea4c25dae8..804681a8b654 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"بازنشانی"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"برداشتن"</string> <string name="guest_resetting" msgid="7822120170191509566">"درحال بازنشانی مهمان…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"جلسه مهمان بازنشانی شود؟"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"با این کار، جلسه مهمان جدیدی شروع خواهد شد و همه برنامهها و دادهها از جلسه کنونی حذف خواهند شد"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"از حالت مهمان خارج میشوید؟"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"با این کار، برنامهها و دادهها از جلسه مهمان کنونی حذف خواهند شد."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"خروج"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"فعالیت مهمان ذخیره شود؟"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"میتوانید فعالیت جلسه کنونی را ذخیره کنید یا همه برنامه و دادهها را حذف کنید"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ذخیره"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"خروج از حالت مهمان"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"بازنشاندن جلسه مهمان"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"خروج مهمان"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"همه فعالیتها هنگام خروج حذف خواهد شد"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"میتوانید فعالیتتان را هنگام خروج ذخیره یا حذف کنید"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"برای حذف فعالیت جلسه در این لحظه، بازنشانی کنید یا میتوانید فعالیت را هنگام خروج ذخیره یا حذف کنید"</string> <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index dd0e50ed5239..734515cc589b 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nollaa"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Poista"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nollataan vierasta…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Valitse kuva"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index a4d9ccc52a76..d62ce169bad5 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Retirer"</string> <string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité en cours…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 921cf2b3b892..eafe539216dd 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Supprimer"</string> <string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index e66d28a13b5c..45a39e84d2a3 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restablecendo sesión de convidado…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 436ad62496e1..abcdcf5e663e 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"રીસેટ કરો"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"કાઢી નાખો"</string> <string name="guest_resetting" msgid="7822120170191509566">"અતિથિને રીસેટ કરી રહ્યાં છીએ…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 01e661782364..23d4727ec277 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करें"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाएं"</string> <string name="guest_resetting" msgid="7822120170191509566">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट किया जा रहा है…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फ़ोटो चुनें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index fdf14b3fab88..994ac1ecce73 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Poništavanje gostujuće sesije…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite li poništiti gostujuću sesiju?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Time će se pokrenuti nova gostujuća sesija i izbrisati sve aplikacije i podaci iz trenutačne sesije."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Napustiti način rada za goste?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time će se izbrisati aplikacije i podaci iz trenutačne gostujuće sesije."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izlaz"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Spremiti aktivnosti gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete spremiti aktivnosti iz ove sesije ili izbrisati sve aplikacije i podatke"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Spremi"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Izlaz iz načina rada za goste"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Poništi gostujuću sesiju"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izlaz iz gostujuće sesije"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve će se aktivnosti izbrisati na izlasku"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Možete spremiti ili izbrisati svoje aktivnosti na izlasku"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Poništite da biste odmah izbrisali aktivnost sesije, a možete i spremiti ili izbrisati aktivnost na izlasku."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir slike"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index e4bc19126baf..be150bf0a959 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Visszaállítás"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eltávolítás"</string> <string name="guest_resetting" msgid="7822120170191509566">"Vendég munkamenet visszaállítása…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 79a657b2e483..b5f817bf468a 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Զրոյացնել"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Հեռացնել"</string> <string name="guest_resetting" msgid="7822120170191509566">"Հյուրի աշխատաշրջանը վերակայվում է…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index bf952849ab4f..6c3660a7107b 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hapus"</string> <string name="guest_resetting" msgid="7822120170191509566">"Mereset tamu …"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 9616618da3cb..56972d23b928 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Endurstilla"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjarlægja"</string> <string name="guest_resetting" msgid="7822120170191509566">"Endurstillir gest…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index f9082911169f..354e657e1dbc 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reimposta"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Rimuovi"</string> <string name="guest_resetting" msgid="7822120170191509566">"Reimpostazione sessione Ospite in corso…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vuoi reimpostare la sessione Ospite?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Verrà avviata una nuova sessione Ospite e verranno eliminati tutti i dati e le app della sessione corrente"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vuoi uscire da modalità Ospite?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Verranno eliminati i dati e le app della sessione Ospite corrente"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Esci"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vuoi salvare l\'attività Ospite?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puoi salvare l\'attività della sessione corrente o eliminare tutti i dati e app"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Elimina"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salva"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Esci dalla modalità Ospite"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reimposta sessione Ospite"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Esci dalla modalità Ospite"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Quando esci verrà eliminata tutta l\'attività"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Quando esci puoi salvare o eliminare la tua attività"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reimposta per eliminare subito l\'attività della sessione oppure puoi salvare o eliminare l\'attività quando esci"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 4304abdfffc5..b3de61596948 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"איפוס"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"הסרה"</string> <string name="guest_resetting" msgid="7822120170191509566">"מתבצע איפוס של הגלישה כאורח…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 4e769e6dc80c..5943a6e11a56 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"リセット"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"削除"</string> <string name="guest_resetting" msgid="7822120170191509566">"ゲストをリセットしています…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ゲスト セッションをリセットしますか?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"新しいゲスト セッションが開始し、現在のセッションのすべてのアプリとデータが削除されます"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ゲストモードを終了しますか?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"現在のゲスト セッションからすべてのアプリとデータが削除されます"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"終了"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ゲストのアクティビティを保存しますか?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"現在のセッションのアクティビティの保存や、すべてのアプリとデータの削除を行えます"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"削除"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"保存"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ゲストモードを終了"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ゲスト セッションをリセット"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ゲストを終了"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"終了時にすべてのアクティビティが削除されます"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"終了時にアクティビティを保存、削除できます"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"リセットして今すぐセッションのアクティビティを削除します(終了時にアクティビティを保存、削除することもできます)"</string> <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index c6a50ac9e565..525a0d235fd2 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"გადაყენება"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ამოშლა"</string> <string name="guest_resetting" msgid="7822120170191509566">"მიმდინარეობს სტუმრის გადაყენება…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"გსურთ სტუმრის სესიის გადაყენება?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ამ ქმედებით დაიწყება სტუმრის ახალი სესია და წაიშლება ყველა აპი და მონაცემი მიმდინარე სესიიდან"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"გსურთ სტუმრის რეჟიმიდან გასვლა?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ეს ქმედება წაშლის აპებსა და მონაცემებს სტუმრის რეჟიმის მიმდინარე სესიიდან"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"გასვლა"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"გსურთ სტუმრის აქტივობის შენახვა?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"შეგიძლიათ შეინახოთ აქტივობა მიმდინარე სესიიდან ან წაშალოთ ყველა აპი და მონაცემი"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"წაშლა"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"შენახვა"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"სტუმრის რეჟიმიდან გასვლა"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"სტუმრის სესიის გადაყენება"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"სტუმრის გასვლა"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"გასვლისას ყველა აქტივობა წაიშლება"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"გასვლისას შეგიძლიათ შეინახოთ ან წაშალოთ თქვენი აქტივობა"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"გადააყენეთ სესიის აქტივობის ახლავე წასაშლელად, ასევე, შეინახეთ ან წაშალეთ აქტივობა გასვლისას"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ფოტოს არჩევა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index f5559d270f0a..b144b1c95de3 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Бастапқы күйге қайтару"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өшіру"</string> <string name="guest_resetting" msgid="7822120170191509566">"Қонақ сеансы бастапқы күйге қайтарылуда…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 7a45212d2139..b74dd3bbcd96 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"កំណត់ឡើងវិញ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ដកចេញ"</string> <string name="guest_resetting" msgid="7822120170191509566">"កំពុងកំណត់ភ្ញៀវឡើងវិញ…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើសរូបថត"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index a5c97cdae7bc..a95377e8073a 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ರೀಸೆಟ್ ಮಾಡಿ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ತೆಗೆದುಹಾಕಿ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ಅತಿಥಿ ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index ccd27c2f96b7..c856b5ceab91 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"재설정"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"삭제"</string> <string name="guest_resetting" msgid="7822120170191509566">"게스트 재설정 중…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 362665b490a4..62d6d0566e6a 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Баштапкы абалга келтирүү"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өчүрүү"</string> <string name="guest_resetting" msgid="7822120170191509566">"Конок сеансы баштапкы абалга келтирилүүдө…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Конок сеансы баштапкы абалга келтирилсинби?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Бул аракет жаңы конок сеансын баштап, учурдагы сеанстагы бардык колдонмолорду жана дайындарды жок кылат"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Конок режиминен чыгасызбы?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Бул учурдагы конок сеансындагы колдонмолорду жана дайындарды жок кылат"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Чыгуу"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Коноктун аракеттери сакталсынбы?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Учурдагы сеанстагы аракеттерди сактап же бардык колдонмолорду жана дайындарды жок кылсаңыз болот"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Өчүрүү"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сактоо"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Конок режиминен чыгуу"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Конок сеансын кайра коюу"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Конок режиминен чыгуу"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Чыксаңыз, бардык аракеттер өчүрүлөт"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Чыгуудан мурун аракеттериңизди сактап же жок кылсаңыз болот"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Сеанстагы аракеттерди азыр жок кылуу үчүн баштапкы абалга келтириңиз, же болбосо чыгуу учурунда аракеттерди сактап же жок кылсаңыз болот"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index b3be17ad5efc..6ad19ad24de6 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ຣີເຊັດ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ລຶບອອກ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ກຳລັງຣີເຊັດແຂກ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ຣີເຊັດໄລຍະເວລາຂອງແຂກບໍ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ນີ້ຈະເລີ່ມໄລຍະເວລາຂອງແຂກໃໝ່ ແລະ ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດອອກຈາກເຊດຊັນປັດຈຸບັນ"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ອອກຈາກໂໝດແຂກບໍ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ນີ້ຈະລຶບແອັບ ແລະ ຂໍ້ມູນອອກຈາກໄລຍະເວລາຂອງແຂກປັດຈຸບັນ"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ອອກ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ບັນທຶກການເຄື່ອນໄຫວແຂກບໍ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ທ່ານສາມາດບັນທຶກການເຄື່ອນໄຫວຈາກເຊດຊັນປັດຈຸບັນ ຫຼື ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດໄດ້"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ລຶບ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ບັນທຶກ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ອອກຈາກໂໝດແຂກ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ຣີເຊັດໄລຍະເວລາຂອງແຂກ"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ອອກຈາກແຂກ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ການເຄື່ອນໄຫວທັງໝົດຈະຖືກລຶບໃນຕອນອອກ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ທ່ານສາມາດບັນທຶກ ຫຼື ລຶບການເຄື່ອນໄຫວຂອງທ່ານໃນຕອນອອກໄດ້"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ຣີເຊັດເພື່ອລຶບການເຄື່ອນໄຫວເຊດຊັນຕອນນີ້ ຫຼື ທ່ານສາມາດບັນທຶກ ຫຼື ລຶບການເຄື່ອນໄຫວໃນຕອນອອກໄດ້"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 250d24af0d38..cd7c9c312e45 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nustatyti iš naujo"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Pašalinti"</string> <string name="guest_resetting" msgid="7822120170191509566">"Svečias nustatomas iš naujo…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 0b3eb9487842..fdf3d7d0763d 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Atiestatīt"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Noņemt"</string> <string name="guest_resetting" msgid="7822120170191509566">"Notiek viesa sesijas atiestatīšana…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index be13fd7f9886..7f0c6082c52f 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетирај"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Отстрани"</string> <string name="guest_resetting" msgid="7822120170191509566">"Се ресетира гостинот…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Изберете фотографија"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index a8a6bec79fba..47b6ff37f742 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"റീസെറ്റ് ചെയ്യുക"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"നീക്കം ചെയ്യുക"</string> <string name="guest_resetting" msgid="7822120170191509566">"അതിഥിയെ റീസെറ്റ് ചെയ്യുന്നു…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"അതിഥി സെഷൻ റീസെറ്റ് ചെയ്യണോ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ഇത് പുതിയൊരു അതിഥി സെഷൻ ആരംഭിക്കുകയും നിലവിലെ സെഷനിൽ നിന്ന് എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കുകയും ചെയ്യും"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കണോ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"നിലവിലെ അതിഥി സെഷനിൽ നിന്ന് ആപ്പുകളും ഡാറ്റയും ഇത് ഇല്ലാതാക്കും"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"പുറത്തുകടക്കുക"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"അതിഥി ആക്റ്റിവിറ്റി സംരക്ഷിക്കണോ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"നിലവിലെ സെഷനിൽ നിന്നുള്ള ആക്റ്റിവിറ്റി സംരക്ഷിക്കാം അല്ലെങ്കിൽ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കാം"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ഇല്ലാതാക്കുക"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"സംരക്ഷിക്കുക"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"അതിഥി സെഷൻ റീസെറ്റ് ചെയ്യുക"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"പുറത്തുകടക്കുമ്പോൾ എല്ലാ ആക്റ്റിവിറ്റിയും ഇല്ലാതാക്കും"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"പുറത്തുകടക്കുമ്പോൾ ആക്റ്റിവിറ്റി സംരക്ഷിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യാം"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"സെഷൻ ആക്റ്റിവിറ്റി ഇപ്പോൾ ഇല്ലാതാക്കാൻ റീസെറ്റ് ചെയ്യുക അല്ലെങ്കിൽ പുറത്തുകടക്കുമ്പോൾ ആക്റ്റിവിറ്റി സംരക്ഷിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യാം"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ഫോട്ടോ തിരഞ്ഞെടുക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index cd41b81381d1..5eef7d386cc1 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Шинэчлэх"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Хасах"</string> <string name="guest_resetting" msgid="7822120170191509566">"Зочныг шинэчилж байна…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Зочны харилцан үйлдлийг шинэчлэх үү?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Энэ нь шинэ зочны харилцан үйлдэл эхлүүлж, одоогийн харилцан үйлдлээс бүх апп болон өгөгдлийг устгана"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Зочны горимоос гарах уу?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Энэ нь одоогийн зочны харилцан үйлдлээс аппууд болон өгөгдлийг устгана"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Гарах"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Зочны үйл ажиллагааг хадгалах уу?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Та одоогийн харилцан үйлдлээс үйл ажиллагаа хадгалах эсвэл бүх апп, өгөгдлийг устгаж болно"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Устгах"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Хадгалах"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Зочны горимоос гарах"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Зочны харилцан үйлдлийг шинэчлэх"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Зочны горимоос гарах"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Бүх үйл ажиллагааг гарах үед устгана"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Та гарахдаа үйл ажиллагаагаа хадгалах эсвэл устгах боломжтой"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Харилцан үйлдлийн үйл ажиллагааг одоо устгахын тулд шинэчлэх эсвэл та гарахдаа үйл ажиллагааг хадгалах, устгах боломжтой"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Зураг сонгох"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 6c2dbe439e96..e7320620c7d8 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करा"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"काढून टाका"</string> <string name="guest_resetting" msgid="7822120170191509566">"अतिथीला रीसेट करत आहे…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"अतिथी सत्र रीसेट करायचे का?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"हे नवीन अतिथी सत्र सुरू करेल आणि सध्याच्या सत्रातील सर्व अॅप्स व डेटा हटवेल"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"अतिथी मोडमधून बाहेर पडायचे का?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"हे सध्याच्या अतिथी सत्रातील अॅप्स आणि डेटा हटवेल"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहेर पडा"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"अतिथी अॅक्टिव्हिटी सेव्ह करायची का?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"सध्याच्या सत्रातील अॅक्टिव्हिटी सेव्ह करू किंवा सर्व अॅप्स व डेटा हटवू शकता"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"हटवा"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेव्ह करा"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"अतिथी मोडमधून बाहेर पडा"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"अतिथी सत्र रीसेट करा"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"अतिथी मोडमधून बाहेर पडा"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"बाहेर पडल्यावर सर्व अॅक्टिव्हिटी हटवली जाईल"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"बाहेर पडल्यावर तुमची अॅक्टिव्हिटी सेव्ह करू किंवा हटवू शकता"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"सत्र अॅक्टिव्हिटी आता हटवण्यासाठी रीसेट करा किंवा तुम्ही बाहेर पडल्यावर अॅक्टिव्हिटी सेव्ह करू अथवा हटवू शकता"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index c989def748c2..ceaf17a1f0d5 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tetapkan semula"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alih keluar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Menetapkan semula tetamu…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Tetapkan semula sesi tetamu?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tindakan ini akan memulakan sesi tetamu baharu dan memadamkan semua apl dan data daripada sesi semasa"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Keluar daripada mod tetamu?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tindakan ini akan memadamkan apl dan data daripada sesi tetamu semasa"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Keluar"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Simpan aktiviti tetamu?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Anda boleh menyimpan aktiviti daripada sesi semasa atau memadamkan semua apl dan data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Padam"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Simpan"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Keluar daripada mod tetamu"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Tetapkan semula sesi tetamu"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Keluar mod tetamu"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Semua aktiviti akan dipadamkan semasa keluar"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktiviti anda boleh disimpan atau dipadam semasa keluar"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Tetapkan semula sesi untuk memadamkan aktiviti sesi sekarang atau anda boleh menyimpan atau memadamkan aktiviti semasa keluar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index a6749ee089df..fd490f988cd8 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -599,6 +599,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ပြင်ဆင်သတ်မှတ်ရန်"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ဖယ်ရှားရန်"</string> <string name="guest_resetting" msgid="7822120170191509566">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်နေသည်…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 57178fd645b4..baf9c54f0cb1 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tilbakestill"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string> <string name="guest_resetting" msgid="7822120170191509566">"Tilbakestiller gjesten …"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index cbe99f3288c9..c21313688af1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रिसेट गर्नुहोस्"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाउनुहोस्"</string> <string name="guest_resetting" msgid="7822120170191509566">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गरिँदै छ…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 8af013617985..2f5313c52c8a 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetten"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwijderen"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gast resetten…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 908fa9686aab..167c69bda3a3 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ରିସେଟ୍ କରନ୍ତୁ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"କାଢ଼ି ଦିଅନ୍ତୁ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ଅତିଥି ସେସନକୁ ରିସେଟ୍ କରାଯାଉଛି…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 0da516c80698..99eae56b0196 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ਰੀਸੈੱਟ ਕਰੋ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ਹਟਾਓ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ਮਹਿਮਾਨ ਨੂੰ ਰੀਸੈੱਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ਕੀ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ਇਸ ਨਾਲ ਨਵਾਂ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਹੋ ਜਾਵੇਗਾ ਅਤੇ ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ਕੀ ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਣਾ ਹੈ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ਇਸ ਨਾਲ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ਬਾਹਰ ਜਾਓ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ਕੀ ਮਹਿਮਾਨ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰਨੀ ਹੈ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ਤੁਸੀਂ ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ਮਿਟਾਓ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ਰੱਖਿਅਤ ਕਰੋ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰੋ"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਾਰੀ ਸਰਗਰਮੀ ਮਿਟਾਈ ਜਾਵੇਗੀ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਆਪਣੀ ਸਭ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਮਿਟਾਉਣ ਹੁਣੇ ਲਈ ਰੀਸੈੱਟ ਕਰੋ ਜਾਂ ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index a72e4df7bb4c..4d12ac983109 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Usuń"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetuję sesję gościa…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Zresetować sesję gościa?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Zostanie uruchomiona nowa sesja gościa. Wszystkie aplikacje i dane z obecnej sesji zostaną usunięte."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Zamknąć tryb gościa?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Wszystkie aplikacje i dane z obecnej sesji gościa zostaną usunięte."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Wyjdź"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Zapisać aktywność gościa?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Możesz zapisać aktywność z obecnej sesji lub usunąć wszystkie aplikacje i dane"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Usuń"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Zapisz"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Zamknij tryb gościa"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Zresetuj sesję gościa"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Zakończ tryb gościa"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Cała aktywność zostanie usunięta po zamknięciu"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Możesz zapisać lub usunąć swoją aktywność podczas zamykania"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Zresetuj, aby usunąć aktywność w sesji w tym momencie. Możesz też ją zapisać lub usunąć podczas zamykania"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index cd7bbfebdfb0..886707515d9a 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index d4ef1039cada..b14dcb1fcd1e 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Repor"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"A repor o convidado…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index cd7bbfebdfb0..886707515d9a 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index e6b18322baac..79fbbf66cbfc 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetați"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eliminați"</string> <string name="guest_resetting" msgid="7822120170191509566">"Se resetează invitatul…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Resetați sesiunea pentru invitați?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Astfel, va începe o nouă sesiune pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieșiți din modul pentru invitați?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieșiți"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvați activitatea invitatului?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Salvați activitatea din sesiunea actuală sau ștergeți aplicațiile și datele"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ștergeți"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvați"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Ieșiți din modul pentru invitați"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Resetați sesiunea pentru invitați"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieșiți din modul pentru invitați"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toate activitățile vor fi șterse la ieșire"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puteți să salvați sau să ștergeți activitatea la ieșire"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetați pentru a șterge acum activitatea din sesiune sau salvați ori ștergeți activitatea la ieșire"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 6b74d5bb5096..6a7f97192546 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Сбросить"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Удалить"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сброс гостевого сеанса…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 7477e52a8833..c4a2da4a1eb9 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"යළි සකසන්න"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ඉවත් කරන්න"</string> <string name="guest_resetting" msgid="7822120170191509566">"අමුත්තා යළි සකසමින්…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index fd2406e53830..bfb45006a91f 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovať"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrániť"</string> <string name="guest_resetting" msgid="7822120170191509566">"Relácia hosťa sa resetuje…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Chcete resetovať reláciu hosťa?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Týmto sa spustí nová relácia hosťa a odstránia sa všetky aplikácie a údaje z aktuálnej relácie"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Chcete skončiť režim pre hostí?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ukončí sa režim pre hostí a odstránia sa aplikácie a údaje z relácie hosťa"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ukončiť"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Chcete uložiť aktivitu hosťa?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Aktivitu v aktuálnej relácii uložte alebo odstráňte všetky aplikácie a údaje"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Odstrániť"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Uložiť"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Ukončiť režim pre hostí"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Resetovať reláciu hosťa"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ukončiť režim pre hostí"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Pri ukončení sa všetka aktivita odstráni"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktivitu môžete pri ukončení uložiť alebo odstrániť"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetovaním ihneď odstráňte aktivitu relácie alebo ju uložte či odstráňte pri ukončení relácie"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index c413bee673c9..5f63f863fec5 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ponastavi"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrani"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ponastavljanje gosta …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite ponastaviti sejo gosta?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"S tem boste začeli novo sejo gosta ter izbrisali vse aplikacije in podatke v trenutni seji."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Zapri način za goste"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"S tem boste izbrisali aplikacije in podatke v trenutni seji gosta."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Zapri"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Želite shraniti dejavnost gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Lahko shranite dejavnost v trenutni seji ali izbrišete vse aplikacije in podatke."</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Shrani"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Zapri način za goste"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Ponastavi sejo gosta"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Zapri sejo gosta"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Ko zaprete sejo, bo vsa dejavnost izbrisana."</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Ko zaprete sejo, lahko shranite ali izbrišete dejavnost."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ponastavite, če želite dejavnost v seji izbrisati zdaj, lahko pa jo shranite ali izbrišete, ko zaprete sejo."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 25f3b978049c..0a113cd5efc0 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Rivendos"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hiq"</string> <string name="guest_resetting" msgid="7822120170191509566">"Vizitori po rivendoset…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Të rivendoset sesioni për vizitorë?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Kjo do të fillojë një sesion të ri për vizitorë dhe do të fshijë të gjitha aplikacionet dhe të dhënat nga sesioni aktual"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Të hiqet modaliteti \"vizitor\"?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Kjo do të fshijë aplikacionet dhe të dhënat nga sesioni aktual për vizitorë"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Dil"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Të ruhet aktiviteti i vizitorit?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ruaj aktivitetin nga sesioni aktual ose fshi të gjitha aplikacionet e të dhënat"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Fshi"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Ruaj"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Dil nga modaliteti \"vizitor\""</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Rivendos sesionin për vizitorë"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Dil nga modaliteti \"vizitor\""</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Të gjitha aktivitetet do të fshihen kur të dalësh"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Mund ta ruash ose ta fshish aktivitetin tënd kur të dalësh"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Rivendose për të fshirë aktivitetin e sesionit tani ose mund ta ruash ose ta fshish aktivitetin kur të dalësh"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 0e2f0d0d14a3..0e9d69de1711 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетуј"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Уклони"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сесија госта се ресетује…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Желите да ресетујете сесију госта?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Тиме ћете покренути нову сесију госта и избрисати све апликације и податке из актуелне сесије"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Изаћи ћете из режима госта?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Тиме ћете избрисати све апликације и податке из актуелне сесије госта"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Изађи"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Сачуваћете активности госта?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Сачувајте активности из актуелне сесије или избришите све апликације и податке"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Избриши"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сачувај"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Изађи из режима госта"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Ресетуј сесију госта"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Изађи из режима госта"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Све активности ће бити избрисане при излазу"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Можете да сачувате или избришете активности при излазу"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ресетујете да бисте избрисали активности сесије одмах или можете да сачувате или избришете активности при излазу"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Изаберите слику"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 430dd4543d45..72d13cc6ea72 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Återställ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ta bort"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gästsessionen återställs …"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 79d9532e0be0..b24fc40514bc 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Badilisha"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ondoa"</string> <string name="guest_resetting" msgid="7822120170191509566">"Inabadilisha kipindi cha mgeni…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index e1bb76631322..d3c2966fba85 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"மீட்டமை"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"அகற்று"</string> <string name="guest_resetting" msgid="7822120170191509566">"கெஸ்ட்டை மீட்டமைக்கிறது…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"கெஸ்ட் அமர்வை ரீசெட் செய்யவா?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"புதிய கெஸ்ட் அமர்வு தொடங்கப்படும், மேலும் தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"கெஸ்ட் முறையிலிருந்து வெளியேறவா?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"வெளியேறு"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"கெஸ்ட் செயல்பாடுகளைச் சேமிக்கவா?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"தற்போதைய அமர்வின் செயல்பாடுகளைச் சேமிக்கலாம் அல்லது ஆப்ஸையும் தரவையும் நீக்கலாம்"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"நீக்கு"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"சேமி"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"கெஸ்ட் பயன்முறையிலிருந்து வெளியேறு"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"கெஸ்ட் அமர்வை ரீசெட் செய்"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"கெஸ்ட் பயன்முறையிலிருந்து வெளியேறு"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"வெளியேறியவுடன் அனைத்துச் செயல்பாடுகளும் நீக்கப்படும்"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"வெளியேறும்போது செயல்பாடுகளைச் சேமிக்கலாம் அல்லது நீக்கலாம்"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"அமர்வின் செயல்பாடுகளை இப்போதே நீக்க ரீசெட் செய்யவும் அல்லது வெளியேறும்போது செயல்பாடுகளைச் சேமிக்கலாம் அல்லது நீக்கலாம்"</string> <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 41db0cc7ba63..029d7a76688b 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"రీసెట్ చేయండి"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"తీసివేయండి"</string> <string name="guest_resetting" msgid="7822120170191509566">"గెస్ట్ సెషన్ను రీసెట్ చేస్తోంది…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"గెస్ట్ సెషన్ను రీసెట్ చేయాలా?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ఇది కొత్త గెస్ట్ సెషన్ను ప్రారంభిస్తుంది, ప్రస్తుత సెషన్ నుండి అన్ని యాప్లు, డేటాను తొలగించండి"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"గెస్ట్ మోడ్ నిష్క్రమించాలా?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ఇది ప్రస్తుత గెస్ట్ సెషన్ నుండి యాప్లు, డేటాను తొలగిస్తుంది"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"నిష్క్రమించండి"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"గెస్ట్ యాక్టివిటీని సేవ్ చేయాలా?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"మీరు సెషన్ నుండి యాక్టివిటీని సేవ్ చేయవచ్చు, అన్ని యాప్లు, డేటాను తొలగించవచ్చు"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"తొలగించండి"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"సేవ్ చేయండి"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"గెస్ట్ మోడ్ నుండి నిష్క్రమణ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"గెస్ట్ సెషన్ను రీసెట్ చేయండి"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"గెస్ట్ మోడ్ నుండి నిష్క్రమించండి"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"నిష్క్రమణ సమయంలో మొత్తం యాక్టివిటీ తొలగించబడుతుంది"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"మీ నిష్క్రమణలో, యాక్టివిటీని సేవ్ చేయవచ్చు లేదా తొలగించవచ్చు"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"సెషన్ యాక్టివిటీని తొలగించడానికి ఇప్పుడే రీసెట్ చేయండి లేదా మీరు నిష్క్రమించేటప్పుడు యాక్టివిటీని సేవ్ చేయవచ్చు లేదా తొలగించవచ్చు"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ఫోటోను ఎంచుకోండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 41ef1e41b90f..c7123b68c802 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"รีเซ็ต"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"นำออก"</string> <string name="guest_resetting" msgid="7822120170191509566">"กำลังรีเซ็ตผู้เข้าร่วม…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"เลือกรูปภาพ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 7d4a4b49bc0d..378543e90f0c 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"I-reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alisin"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nire-reset ang bisita…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pumili ng larawan"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 8ef368cbd37b..ff28c6190067 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırla"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kaldır"</string> <string name="guest_resetting" msgid="7822120170191509566">"Misafir oturumu sıfırlanıyor…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 0f4ee0d05806..1c85dfd0c0f2 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скинути"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Вилучити"</string> <string name="guest_resetting" msgid="7822120170191509566">"Скидання сеансу в режимі \"Гість\"…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 5c535096ed97..acd3202dd7b5 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ری سیٹ کریں"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ہٹائیں"</string> <string name="guest_resetting" msgid="7822120170191509566">"مہمان کو ری سیٹ کرنا…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 52cc9cbd7c03..19500f8ad6ca 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -598,6 +598,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tiklash"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Olib tashlash"</string> <string name="guest_resetting" msgid="7822120170191509566">"Mehmon seansi tiklanmoqda…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Mehmon seansi tiklansinmi?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bunda yangi mehmon seansi ishga tushadi va joriy seans ilova va maʼlumotlari tozalanadi"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Mehmon rejimidan chiqasizmi?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bunda joriy mehmon seansidagi ilova va ularning maʼlumotlari tozalanadi"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Chiqish"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Mehmon faoliyati saqlansinmi?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Joriy seansdagi faoliyatni saqlash yoki barcha ilova va maʼlumotlarni tozalash mumkin"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Oʻchirish"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Saqlash"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Mehmon rejimidan chiqish"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Mehmon seansini tiklash"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Mehmon rejimidan chiqish"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Chiqish vaqtida faoliyat butunlay tozalanadi"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Chiqish vaqtida faoliyatni saqlash yoki tozalash mumkin"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Faoliyat hozir tozalanib tiklanishi yoki chiqish vaqtida saqlanishi yoki tozalanishi mumkin"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Surat tanlash"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index dac955e33e48..d2cdb1e6d2f4 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Đặt lại"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Xoá"</string> <string name="guest_resetting" msgid="7822120170191509566">"Đang đặt lại phiên khách…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 5280f10b767d..5006d6cf298d 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重置"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重置访客会话…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 1cd9d227fddc..7b6921c1c382 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 8a85ed1a410d..36ebb248f9b4 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 6ef85d235bc8..67625e4b7009 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -598,6 +598,36 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Setha kabusha"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Susa"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ukusetha kabusha isimenywa…"</string> + <!-- no translation found for guest_reset_and_restart_dialog_title (3396657008451616041) --> + <skip /> + <!-- no translation found for guest_reset_and_restart_dialog_message (2764425635305200790) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title (1846494656849381804) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message (1743218864242719783) --> + <skip /> + <!-- no translation found for guest_exit_dialog_button (1736401897067442044) --> + <skip /> + <!-- no translation found for guest_exit_dialog_title_non_ephemeral (7675327443743162986) --> + <skip /> + <!-- no translation found for guest_exit_dialog_message_non_ephemeral (223385323235719442) --> + <skip /> + <!-- no translation found for guest_exit_clear_data_button (3425812652180679014) --> + <skip /> + <!-- no translation found for guest_exit_save_data_button (3690974510644963547) --> + <skip /> + <!-- no translation found for guest_exit_button (5774985819191803960) --> + <skip /> + <!-- no translation found for guest_reset_button (2515069346223503479) --> + <skip /> + <!-- no translation found for guest_exit_quick_settings_button (1912362095913765471) --> + <skip /> + <!-- no translation found for guest_notification_ephemeral (7263252466950923871) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral (6843799963012259330) --> + <skip /> + <!-- no translation found for guest_notification_non_ephemeral_non_first_login (8009307983766934876) --> + <skip /> <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Khetha isithombe"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index df2685db17f5..8ef712a8f7a0 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1438,6 +1438,44 @@ <string name="guest_remove_guest_confirm_button">Remove</string> <!-- Status message indicating the device is in the process of resetting the guest user. [CHAR_LIMIT=NONE] --> <string name="guest_resetting">Resetting guest\u2026</string> + <!-- Dialog title on action reset and restart guest [CHAR LIMIT=60] --> + <string name="guest_reset_and_restart_dialog_title">Reset guest session?</string> + <!-- Dialog message on action reset and restart guest [CHAR LIMIT=160] --> + <string name="guest_reset_and_restart_dialog_message">This will start a new guest + session and delete all apps and data from the current session</string> + <!-- Dialog title on action exit guest (ephemeral guest) [CHAR LIMIT=32] --> + <string name="guest_exit_dialog_title">Exit guest mode?</string> + <!-- Dialog message on action exit guest (ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_dialog_message">This will delete + apps and data from the current guest session</string> + <!-- Dialog button on action exit guest (ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_dialog_button">Exit</string> + <!-- Dialog title on action exit guest (non-ephemeral guest) [CHAR LIMIT=32] --> + <string name="guest_exit_dialog_title_non_ephemeral">Save guest activity?</string> + <!-- Dialog message on action exit guest (non-ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_dialog_message_non_ephemeral">You can save activity from + the current session or delete all apps and data</string> + <!-- Button on guest exit, clear data (non-ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_clear_data_button">Delete</string> + <!-- Button on guest exit, save data (non-ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_save_data_button">Save</string> + <!-- Label for button in confirmation dialog when exiting guest user [CHAR LIMIT=35] --> + <string name="guest_exit_button">Exit guest mode</string> + <!-- Label for button in confirmation dialog when resetting guest user [CHAR LIMIT=35] --> + <string name="guest_reset_button">Reset guest session</string> + <!-- Label for guest icon in quick settings user switcher [CHAR LIMIT=35] --> + <string name="guest_exit_quick_settings_button">Exit guest</string> + <!-- Message of the notification when guest mode is entered + and it's a ephemeral guest [CHAR LIMIT=60] --> + <string name="guest_notification_ephemeral">All activity will be deleted on exit</string> + <!-- Message of the notification when guest mode is entered + and it's not a ephemeral guest and it's a first time guest login [CHAR LIMIT=60] --> + <string name="guest_notification_non_ephemeral">You can save or delete your activity on exit</string> + <!-- Message of the notification when guest mode is entered + and it's not a ephemeral guest and it's not a first time guest login [CHAR LIMIT=NONE] --> + <string name="guest_notification_non_ephemeral_non_first_login">Reset to delete session + activity now, or you can save or delete activity on exit</string> + <!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] --> <string name="user_image_take_photo">Take a photo</string> <!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] --> diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java index f28572f5f71d..69484ed527b5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java @@ -37,9 +37,9 @@ import android.widget.ImageView; import androidx.fragment.app.FragmentActivity; import com.android.settingslib.R; +import com.android.settingslib.RestrictedLockUtils; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; @@ -55,7 +55,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; @RunWith(RobolectricTestRunner.class) -@Ignore public class EditUserInfoControllerTest { private static final int MAX_USER_NAME_LENGTH = 100; @@ -87,6 +86,11 @@ public class EditUserInfoControllerTest { } @Override + RestrictedLockUtils.EnforcedAdmin getChangePhotoAdminRestriction(Context context) { + return null; + } + + @Override boolean isChangePhotoRestrictedByBase(Context context) { return mPhotoRestrictedByBase; } @@ -98,7 +102,7 @@ public class EditUserInfoControllerTest { mActivity = spy(ActivityController.of(new FragmentActivity()).get()); mActivity.setTheme(R.style.Theme_AppCompat_DayNight); mController = new TestEditUserInfoController(); - mPhotoRestrictedByBase = true; + mPhotoRestrictedByBase = false; } @Test @@ -262,7 +266,7 @@ public class EditUserInfoControllerTest { @Test public void createDialog_canNotChangePhoto_nullPhotoController() { - mPhotoRestrictedByBase = false; + mPhotoRestrictedByBase = true; mController.createDialog(mActivity, mActivityStarter, mCurrentIcon, "test", "title", null, null); diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index acb33c356a8b..75068cbcbb4b 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -317,6 +317,9 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_WIDTH, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.Wearable.WET_MODE_ON, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.Wearable.COOLDOWN_MODE_ON, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.Wearable.TOUCH_AND_HOLD_WATCH_FACE, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.Wearable.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 077337cdc8c3..700c04cf34ec 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -5220,7 +5220,7 @@ public class SettingsProvider extends ContentProvider { .getResources() .getBoolean(R.bool.def_wearable_hotwordDetectionEnabled)); initGlobalSettingsDefaultValForWearLocked( - Global.Wearable.SMART_REPLIES_ENABLED, false); + Global.Wearable.SMART_REPLIES_ENABLED, true); Setting locationMode = getSecureSettingsLocked(userId).getSettingLocked(Secure.LOCATION_MODE); initGlobalSettingsDefaultValForWearLocked( diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index d122cf5acab3..f06e34995789 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -422,6 +422,7 @@ public class SettingsBackupTest { Settings.Global.RADIO_NFC, Settings.Global.RADIO_WIFI, Settings.Global.RADIO_WIMAX, + Settings.Global.REMOVE_GUEST_ON_EXIT, Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS, Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT, Settings.Global.RESTRICTED_NETWORKING_MODE, @@ -664,7 +665,9 @@ public class SettingsBackupTest { Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED, Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER, Settings.Global.Wearable.WET_MODE_ON, - Settings.Global.Wearable.COOLDOWN_MODE_ON); + Settings.Global.Wearable.COOLDOWN_MODE_ON, + Settings.Global.Wearable.TOUCH_AND_HOLD_WATCH_FACE, + Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED); private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS = newHashSet( diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 3b862ffbbd9e..8f1cc2560dc6 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -683,8 +683,8 @@ <!-- Permission required for CTS test - CtsAppEnumerationTestCases --> <uses-permission android:name="android.permission.MAKE_UID_VISIBLE" /> - <!-- Permission required for CTS test - CtsKeystoreTestCases --> - <uses-permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION" /> + <!-- Permission required for CTS test - CtsInputTestCases --> + <uses-permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY" /> <!-- Permission required for CTS test - CtsDevicePolicyManagerTestCases --> <uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" /> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 1ce4c64fd8b8..68679c794c35 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -572,6 +572,7 @@ public class BugreportProgressService extends Service { break; case INTENT_BUGREPORT_DONE: maybeShowWarningMessageAndCloseNotification(id); + break; case INTENT_BUGREPORT_CANCEL: cancel(id); break; @@ -843,16 +844,11 @@ public class BugreportProgressService extends Service { PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); } - @GuardedBy("mLock") - private void stopProgressLocked(int id) { - stopProgressLocked(id, /* cancelNotification */ true); - } - /** * Finalizes the progress on a given bugreport and cancel its notification. */ @GuardedBy("mLock") - private void stopProgressLocked(int id, boolean cancelNotification) { + private void stopProgressLocked(int id) { if (mBugreportInfos.indexOfKey(id) < 0) { Log.w(TAG, "ID not watched: " + id); } else { @@ -862,12 +858,10 @@ public class BugreportProgressService extends Service { // Must stop foreground service first, otherwise notif.cancel() will fail below. stopForegroundWhenDoneLocked(id); - if (cancelNotification) { - Log.d(TAG, "stopProgress(" + id + "): cancel notification"); - NotificationManager.from(mContext).cancel(id); - } else { - Log.d(TAG, "stopProgress(" + id + ")"); - } + + Log.d(TAG, "stopProgress(" + id + "): cancel notification"); + NotificationManager.from(mContext).cancel(id); + stopSelfWhenDoneLocked(); } @@ -1112,30 +1106,7 @@ public class BugreportProgressService extends Service { return; } - if (mIsWatch) { - // Wear wants to send the notification directly and not wait for the user to tap on the - // notification. - triggerShareBugreportAndLocalNotification(info); - } else { - triggerLocalNotification(info); - } - } - - /** - * Responsible for starting the bugerport sharing process and posting a notification which - * shows that the bugreport has been taken and that the sharing process has kicked-off. - */ - private void triggerShareBugreportAndLocalNotification(final BugreportInfo info) { - boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt"); - if (!isPlainText) { - // Already zipped, share it right away. - shareBugreport(info.id, info, /* showWarning */ false, - /* cancelNotificationWhenStoppingProgress */ false); - sendBugreportNotification(info, mTakingScreenshot); - } else { - // Asynchronously zip the file first, then share it. - shareAndPostNotificationForZippedBugreport(info, mTakingScreenshot); - } + triggerLocalNotification(info); } /** @@ -1249,16 +1220,14 @@ public class BugreportProgressService extends Service { } private void shareBugreport(int id, BugreportInfo sharedInfo) { - shareBugreport(id, sharedInfo, !hasUserDecidedNotToGetWarningMessage(), - /* cancelNotificationWhenStoppingProgress */ true); + shareBugreport(id, sharedInfo, !hasUserDecidedNotToGetWarningMessage()); } /** * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE} * intent, but issuing a warning dialog the first time. */ - private void shareBugreport(int id, BugreportInfo sharedInfo, boolean showWarning, - boolean cancelNotificationWhenStoppingProgress) { + private void shareBugreport(int id, BugreportInfo sharedInfo, boolean showWarning) { MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SHARE); BugreportInfo info; synchronized (mLock) { @@ -1307,7 +1276,7 @@ public class BugreportProgressService extends Service { } synchronized (mLock) { // ... and stop watching this process. - stopProgressLocked(id, cancelNotificationWhenStoppingProgress); + stopProgressLocked(id); } } @@ -1362,7 +1331,7 @@ public class BugreportProgressService extends Service { PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .setDeleteIntent(newCancelIntent(mContext, info)); } else { - // Device is a watch. + // Device is a watch if (hasUserDecidedNotToGetWarningMessage()) { // No action button needed for the notification. User can swipe to dimiss. builder.setActions(new Action[0]); @@ -1433,24 +1402,6 @@ public class BugreportProgressService extends Service { } /** - * Zips a bugreport, shares it, and sends for it a bugreport notification. - */ - private void shareAndPostNotificationForZippedBugreport(final BugreportInfo info, - final boolean takingScreenshot) { - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - Looper.prepare(); - zipBugreport(info); - shareBugreport(info.id, info, /* showWarning */ false, - /* cancelNotificationWhenStoppingProgress */ false); - sendBugreportNotification(info, mTakingScreenshot); - return null; - } - }.execute(); - } - - /** * Zips a bugreport file, returning the path to the new file (or to the * original in case of failure). */ diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index f05c1e2e76f2..de9e1f4ccec5 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -125,6 +125,7 @@ android_library { ], manifest: "AndroidManifest.xml", + javacflags: ["-Adagger.fastInit=enabled"], kotlincflags: ["-Xjvm-default=enable"], plugins: ["dagger2-compiler"], diff --git a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml index 779ab816d925..9b43cf64f116 100644 --- a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml +++ b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:fillColor="@color/media_dialog_item_main_content" + android:fillColor="@color/media_dialog_inactive_item_main_content" android:pathData="M12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,12zM12,20q3.325,0 5.663,-2.337Q20,15.325 20,12t-2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,3.325 2.338,5.663Q8.675,20 12,20z"/> </vector> diff --git a/packages/SystemUI/res/drawable/media_output_status_check.xml b/packages/SystemUI/res/drawable/media_output_status_check.xml index 5fbc42b245b8..1b750f8959b9 100644 --- a/packages/SystemUI/res/drawable/media_output_status_check.xml +++ b/packages/SystemUI/res/drawable/media_output_status_check.xml @@ -21,6 +21,6 @@ android:viewportHeight="24" android:tint="?attr/colorControlNormal"> <path - android:fillColor="@color/media_dialog_item_main_content" + android:fillColor="@color/media_dialog_item_status" android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/> </vector> diff --git a/packages/SystemUI/res/drawable/media_output_status_failed.xml b/packages/SystemUI/res/drawable/media_output_status_failed.xml index 0599e239a9ee..05c635833441 100644 --- a/packages/SystemUI/res/drawable/media_output_status_failed.xml +++ b/packages/SystemUI/res/drawable/media_output_status_failed.xml @@ -21,6 +21,6 @@ android:viewportHeight="24" android:tint="?attr/colorControlNormal"> <path - android:fillColor="@color/media_dialog_item_main_content" + android:fillColor="@color/media_dialog_inactive_item_main_content" android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> </vector> diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml index 1efb4796b5b7..39c5c4564e19 100644 --- a/packages/SystemUI/res/layout/media_output_dialog.xml +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -137,7 +137,7 @@ style="@style/Widget.Dialog.Button.BorderButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/media_output_dialog_button_stop_casting" + android:text="@string/keyboard_key_media_stop" android:visibility="gone"/> <Space diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml index d39b0d53c743..eeb37c7b062d 100644 --- a/packages/SystemUI/res/layout/media_output_list_item.xml +++ b/packages/SystemUI/res/layout/media_output_list_item.xml @@ -33,7 +33,7 @@ android:layout_height="match_parent" android:background="@drawable/media_output_item_background" android:layout_gravity="center_vertical|start"> - <com.android.systemui.media.dialog.MediaOutputSeekbar + <SeekBar android:id="@+id/volume_seekbar" android:splitTrack="false" android:visibility="gone" @@ -83,7 +83,7 @@ android:ellipsize="end" android:maxLines="1" android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:textColor="@color/media_dialog_item_main_content" + android:textColor="@color/media_dialog_inactive_item_main_content" android:textSize="16sp"/> <TextView android:id="@+id/subtitle" @@ -91,7 +91,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="1" - android:textColor="@color/media_dialog_item_main_content" + android:textColor="@color/media_dialog_inactive_item_main_content" android:textSize="14sp" android:fontFamily="@*android:string/config_bodyFontFamily" android:visibility="gone"/> @@ -127,7 +127,6 @@ android:layout_gravity="right|center" android:button="@drawable/ic_circle_check_box" android:visibility="gone" - android:clickable="false" /> </FrameLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index e56a810fc1f1..ba2b304b59ab 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, gaan voort"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gasmodus"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Jy is in gasmodus"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Voeg nuwe gebruiker by?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Wanneer jy \'n nuwe gebruiker byvoeg, moet daardie persoon hul spasie opstel.\n\nEnige gebruiker kan programme vir al die ander gebruikers opdateer."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"As ’n nuwe gebruiker bygevoeg word, sal gasmodus verlaat word en sal alle programme en data in die huidige gastesessie uitgevee word."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Gebruikerlimiet is bereik"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Jy kan tot <xliff:g id="COUNT">%d</xliff:g> gebruikers byvoeg.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bind nuwe toestel saam"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Maak die program oop om hierdie sessie uit te saai."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende program"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hou op uitsaai"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Saai uit"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mense in jou omtrek met versoenbare Bluetooth-toestelle kan na die media luister wat jy uitsaai"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index e9b2a1c046f8..83e720bb8387 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"አዎ፣ ቀጥል"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"የእንግዳ ሁነታ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"በእንግዳ ሁኔታ ውስጥ ነዎት"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"አዲስ ተጠቃሚ ይታከል?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"እርስዎ አንድ አዲስ ተጠቃሚ ሲያክሉ ያ ሰው የራሱ ቦታ ማዘጋጀት አለበት።\n\nማንኛውም ተጠቃሚ መተግበሪያዎችን ለሌሎች ተጠቃሚዎች ሁሉ ሊያዘምን ይችላል።"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"አዲስ ተጠቃሚ ማከል ከእንግዳ ሁነታ ወጥቶ ሁሉንም መተግበሪያዎች እና ውሂብ አሁን ካለው የእንግዳ ክፍለ ጊዜ ይሰርዛል።"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"የተጠቃሚ ገደብ ላይ ተደርሷል"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ተጠቃሚዎች ብቻ ናቸው ሊፈጠሩ የሚችሉት።</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"አዲስ መሣሪያ ያጣምሩ"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ይህን ክፍለ ጊዜ cast ለማድረግ፣ እባክዎ መተግበሪያውን ይክፈቱ።"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"የማይታወቅ መተግበሪያ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Cast ማድረግ አቁም"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ማሰራጨት እንዴት እንደሚሠራ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ስርጭት"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ተኳሃኝ የብሉቱዝ መሣሪያዎች ያላቸው በአቅራቢያዎ ያሉ ሰዎች እርስዎ እያሰራጩት ያሉትን ሚዲያ ማዳመጥ ይችላሉ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 91a033885817..9a3c0524fa09 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -346,6 +346,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"نعم، متابعة"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"هل تريد إضافة مستخدم جديد؟"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"عند إضافة مستخدم جديد، عليه إعداد مساحته.\n\nويُمكن لأي مستخدم تحديث التطبيقات لجميع المستخدمين الآخرين."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"تم الوصول إلى أقصى عدد للمستخدمين"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="zero">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدم.</item> @@ -859,7 +867,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"إقران جهاز جديد"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"لبث هذه الجلسة، يُرجى فتح التطبيق"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"تطبيق غير معروف"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"إيقاف البث"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"كيفية عمل البث"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"البث"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"يمكن للأشخاص القريبين منك الذين لديهم أجهزة متوافقة تتضمّن بلوتوث الاستماع إلى الوسائط التي تبثها."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index ce58c7cc6cb1..8bf3e81262b8 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"হয়, অব্যাহত ৰাখক"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"অতিথি ম’ড"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"আপুনি অতিথি ম’ডত আছে"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"নতুন ব্যৱহাৰকাৰী যোগ কৰিবনে?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"আপুনি যেতিয়া এজন নতুন ব্যৱহাৰকাৰী যোগ কৰে, তেওঁ নিজৰ স্থান ছেট আপ কৰা প্ৰয়োজন।\n\nযিকোনো ব্যৱহাৰকাৰীয়ে নিজৰ লগতে আন ব্যৱহাৰকাৰীৰো এপ্ আপডে’ট কৰিব পাৰে।"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"এগৰাকী নতুন ব্যৱহাৰকাৰীক যোগ দিয়াটোৱে অতিথি ম’ডৰ পৰা বাহিৰ কৰিব আৰু বৰ্তমানৰ অতিথিৰ ছেশ্বনটোৰ পৰা আটাইবোৰ এপ্ আৰু ডেটা মচিব।"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"অধিকতম ব্যৱহাৰকাৰী সৃষ্টি কৰা হ’ল"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">আপুনি <xliff:g id="COUNT">%d</xliff:g> জনলৈকে ব্যৱহাৰকাৰী যোগ কৰিব পাৰে।</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইচ পেয়াৰ কৰক"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"এই ছেশ্বনটো কাষ্ট কৰিবলৈ, অনুগ্ৰহ কৰি এপ্টো খোলক"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"অজ্ঞাত এপ্"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"কাষ্ট বন্ধ কৰক"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"সম্প্ৰচাৰ কৰাটোৱে কেনেকৈ কাম কৰে"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্ৰচাৰ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"সমিল ব্লুটুথ ডিভাইচৰ সৈতে আপোনাৰ নিকটৱৰ্তী স্থানত থকা লোকসকলে আপুনি সম্প্ৰচাৰ কৰা মিডিয়াটো শুনিব পাৰে"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 5a62066936d8..9890de30309b 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Bəli, davam edin"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Qonaq rejimi"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Qonaq rejimindəsiniz"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Yeni istifadəçi əlavə edilsin?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Yeni istifadəçi əlavə etdiyiniz zaman həmin şəxs öz yerini quraşdırmalıdır. \n\n İstənilən istifadəçi bütün digər istifadəçilərdən olan tətbiqləri güncəlləşdirə bilər."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yeni istifadəçi əlavə edildikdə qonaq rejimindən çıxılacaq və cari qonaq sessiyasındakı bütün tətbiqlər və data silinəcək."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"İstifadəçi limitinə çatmısınız"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Maksimum <xliff:g id="COUNT">%d</xliff:g> istifadəçi əlavə edə bilərsiniz.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Cihaz əlavə edin"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Bu sessiyanı yayımlamaq üçün tətbiqi açın."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Naməlum tətbiq"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayımı dayandırın"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayım necə işləyir"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Yayım"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Uyğun Bluetooth cihazları olan yaxınlığınızdakı insanlar yayımladığınız medianı dinləyə bilər"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index a5ad4f71185d..bc1577083fd6 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -340,6 +340,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim gosta"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Koristite režim gosta"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Dodajete novog korisnika?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kada dodate novog korisnika, ta osoba treba da podesi svoj prostor.\n\nSvaki korisnik može da ažurira aplikacije za sve ostale korisnike."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodavanjem novog korisnika izaći ćete iz režima gosta i izbrisaćete sve aplikacije i podatke iz aktuelne sesije gosta."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dostignut maksimalni broj korisnika"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item> @@ -840,7 +845,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Da biste prebacivali ovu sesiju, otvorite aplikaciju."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi prebacivanje"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcioniše emitovanje"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Emitovanje"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ljudi u blizini sa kompatibilnim Bluetooth uređajima mogu da slušaju medijski sadržaj koji emitujete"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 3338388c20a1..4a1bd7b6f3ad 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -342,6 +342,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Так, працягнуць"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Дадаць новага карыстальніка?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Пасля стварэння профілю яго трэба наладзіць.\n\nЛюбы карыстальнік прылады можа абнаўляць праграмы ўсіх іншых карыстальнікаў."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Дасягнуты ліміт карыстальнікаў"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальніка.</item> @@ -847,7 +855,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спалучыць з новай прыладай"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Для трансляцыі гэтага сеанса адкрыйце праграму."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Невядомая праграма"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Спыніць трансляцыю"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як адбываецца трансляцыя"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляцыя"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Людзі паблізу, у якіх ёсць прылады з Bluetooth, змогуць праслухваць мультымедыйнае змесціва, якое вы трансліруеце"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 67e838bfa8cb..71832f8de9fb 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продължавам"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Да се добави ли нов потребител?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Когато добавите нов потребител, той трябва да настрои работното си пространство.\n\nВсеки потребител може да актуализира приложенията за всички останали потребители."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнахте огранич. за потребители"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Можете да добавите до <xliff:g id="COUNT">%d</xliff:g> потребители.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Сдвояване на ново устройство"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"За да предавате тази сесия, моля, отворете приложението."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Неизвестно приложение"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Спиране на предаването"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работи предаването"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Предаване"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Хората в близост със съвместими устройства с Bluetooth могат да слушат мултимедията, която предавате"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index d763748d504f..d56724b5749f 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি চালিয়ে যেতে চান?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"হ্যাঁ, চালিয়ে যান"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"নতুন ব্যবহারকারীকে যোগ করবেন?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"আপনি একজন নতুন ব্যবহারকারী যোগ করলে তাকে তার জায়গা সেট-আপ করে নিতে হবে৷\n\nযেকোনও ব্যবহারকারী অন্য সব ব্যবহারকারীর জন্য অ্যাপ আপডেট করতে পারবেন৷"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"আর কোনও প্রোফাইল যোগ করা যাবে না"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">আপনি <xliff:g id="COUNT">%d</xliff:g> জন পর্যন্ত ব্যবহারকারীর প্রোফাইল যোগ করতে পারেন।</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইস পেয়ার করুন"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"এই সেশন কাস্ট করার জন্য, অ্যাপ খুলুন।"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"অজানা অ্যাপ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"কাস্ট করা বন্ধ করুন"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ব্রডকাস্ট কীভাবে কাজ করে"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্রচার করুন"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"আশপাশে লোকজন যাদের মানানসই ব্লুটুথ ডিভাইস আছে, তারা আপনার ব্রডকাস্ট করা মিডিয়া শুনতে পারবেন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 11b9f429c301..6614a99bfea5 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -340,6 +340,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Način rada za goste"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Upotrebljavate način rada za goste"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Dodati novog korisnika?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kada dodate novog korisnika, ta osoba treba postaviti svoj prostor.\n\nSvaki korisnik može ažurirati aplikacije za sve ostale korisnike."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ako dodate novog korisnika, napustit ćete način rada za goste i izbrisat će se svi podaci i aplikacije iz trenutačne gostujuće sesije."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dostignut limit za broj korisnika"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item> @@ -840,7 +845,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Da emitirate ovu sesiju, otvorite aplikaciju."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi emitiranje"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcionira emitiranje"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Emitirajte"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u vašoj blizini s kompatibilnim Bluetooth uređajima mogu slušati medijske sadržaje koje emitirate"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 13b53fb9b8a1..8cf9deec05bd 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continua"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Vols afegir un usuari nou?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Quan s\'afegeix un usuari nou, aquest usuari ha de configurar-se l\'espai.\n\nQualsevol usuari pot actualitzar les aplicacions de la resta d\'usuaris."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"S\'ha assolit el límit d\'usuaris"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Pots afegir fins a <xliff:g id="COUNT">%d</xliff:g> usuaris.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincula un dispositiu nou"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Per emetre aquesta sessió, obre l\'aplicació."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicació desconeguda"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Atura l\'emissió"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Com funciona l\'emissió"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Emet"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les persones properes amb dispositius Bluetooth compatibles poden escoltar el contingut multimèdia que emets"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 69e5fcc397d2..439201860128 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -342,6 +342,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ano, pokračovat"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Přidat nového uživatele?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Když přidáte nového uživatele, musí si nastavit vlastní prostor.\n\nJakýkoli uživatel může aktualizovat aplikace všech ostatních uživatelů."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Bylo dosaženo limitu uživatelů"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatele.</item> @@ -847,7 +855,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovat nové zařízení"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pokud chcete odesílat relaci, otevřete aplikaci."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Neznámá aplikace"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zastavit odesílání"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak vysílání funguje"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Vysílání"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lidé ve vašem okolí s kompatibilními zařízeními Bluetooth mohou poslouchat média, která vysíláte"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 9fed9c5220dc..7bdaf674d458 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsæt"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Vil du tilføje en ny bruger?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Når du tilføjer en ny bruger, skal personen konfigurere sit område.\n\nAlle brugere kan opdatere apps for alle de andre brugere."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Grænsen for antal brugere er nået"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Du kan tilføje op til <xliff:g id="COUNT">%d</xliff:g> bruger.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Par ny enhed"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Åbn appen for at caste denne session."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ukendt app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop med at caste"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Sådan fungerer udsendelser"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Udsendelse"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i nærheden, som har kompatible Bluetooth-enheder, kan lytte til det medie, du udsender"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 2aefa8fb449c..b92bd62a01f0 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, weiter"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Neuen Nutzer hinzufügen?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Wenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Nutzerlimit erreicht"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Du kannst bis zu <xliff:g id="COUNT">%d</xliff:g> Nutzer hinzufügen.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Neues Gerät koppeln"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Öffne zum Streamen dieser Sitzung die App."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unbekannte App"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Streaming beenden"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Funktionsweise von Nachrichten an alle"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Nachricht an alle"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personen, die in der Nähe sind und kompatible Bluetooth-Geräten haben, können sich die Medien anhören, die du per Nachricht an alle sendest"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 4b5c7b026dc7..787f24f767e3 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ναι, συνέχεια"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Προσθήκη νέου χρήστη;"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Κατά την προσθήκη ενός νέου χρήστη, αυτός θα πρέπει να ρυθμίσει τον χώρο του.\n\nΟποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Συμπληρώθηκε το όριο χρηστών"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Μπορείτε να προσθέσετε έως <xliff:g id="COUNT">%d</xliff:g> χρήστες.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Σύζευξη νέας συσκευής"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Για μετάδοση της περιόδου σύνδεσης, ανοίξτε την εφαρμογή."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Άγνωστη εφαρμογή"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Διακοπή μετάδοσης"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Πώς λειτουργεί η μετάδοση"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Μετάδοση"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Οι άνθρωποι με συμβατές συσκευές Bluetooth που βρίσκονται κοντά σας μπορούν να ακούσουν το μέσο που μεταδίδετε."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index bedeea1a3dab..608bb4c20c52 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Add new user?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index d886e9fb396c..caa12839a0f4 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Add new user?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index bedeea1a3dab..608bb4c20c52 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Add new user?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index bedeea1a3dab..608bb4c20c52 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Add new user?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 2da2bdf43331..f8f7312ea3ce 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start over"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Add new user?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">"\n\nAdding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 51b5b6634dc4..39b351988554 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continuar"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"¿Agregar usuario nuevo?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Cuando agregas un nuevo usuario, esa persona debe configurar su espacio.\n\nCualquier usuario puede actualizar las aplicaciones del resto de los usuarios."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Alcanzaste el límite de usuarios"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Puedes agregar hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo nuevo"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para transmitir esta sesión, abre la app"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconocida"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Detener transmisión"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la transmisión"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisión"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que transmites"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 8118f54b3aa0..b15ce64bc78d 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continuar"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo invitados"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás en modo invitados"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"¿Añadir nuevo usuario?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Al añadir un nuevo usuario, este debe configurar su espacio.\n\nCualquier usuario puede actualizar las aplicaciones del resto de usuarios."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si añades un usuario nuevo, saldrás del modo invitados y se eliminarán todas las aplicaciones y datos de la sesión de invitado actual."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Has alcanzado el límite de usuarios"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Puedes añadir hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Emparejar nuevo dispositivo"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para enviar esta sesión, abre la aplicación."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicación desconocida"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dejar de enviar contenido"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Emisión"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que emites"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index acadbed07a43..9b05c6d01271 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Jah, jätka"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Kas lisada uus kasutaja?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kui lisate uue kasutaja, siis peab ta seadistama oma ruumi.\n\nIga kasutaja saab värskendada rakendusi kõigi kasutajate jaoks."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Kasutajate limiit on täis"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Võite lisada kuni <xliff:g id="COUNT">%d</xliff:g> kasutajat.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uue seadme sidumine"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Selle seansi ülekandmiseks avage rakendus."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Tundmatu rakendus"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Lõpeta ülekanne"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kuidas ülekandmine toimib?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Ülekanne"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Teie läheduses olevad inimesed, kellel on ühilduvad Bluetooth-seadmed, saavad kuulata teie ülekantavat meediat"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index d609555dfcff..6f0034ae9727 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Bai, jarraitu"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Beste erabiltzaile bat gehitu?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Erabiltzaile bat gehitzen duzunean, erabiltzaile horrek bere eremua konfiguratu beharko du.\n\nEdozein erabiltzailek egunera ditzake beste erabiltzaile guztien aplikazioak."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Erabiltzaile-mugara iritsi zara"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Gehienez, <xliff:g id="COUNT">%d</xliff:g> erabiltzaile gehi ditzakezu.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parekatu beste gailu batekin"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Saioa ireki nahi baduzu, ireki aplikazioa."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikazio ezezaguna"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Utzi igortzeari"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Iragarri"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth bidezko gailu bateragarriak dituzten inguruko pertsonek iragartzen ari zaren multimedia-edukia entzun dezakete"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index dd4c43f0464e..014d6d202cce 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا میخواهید جلسهتان را ادامه دهید؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"بله، ادامه داده شود"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"حالت مهمان"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"در حالت مهمان هستید"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"کاربر جدیدی اضافه میکنید؟"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"وقتی کاربر جدیدی اضافه میکنید آن فرد باید فضای خودش را تنظیم کند.\n\nهر کاربری میتواند برنامهها را برای همه کاربران دیگر بهروزرسانی کند."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"با افزودن کاربر جدید، از حالت مهمان خارج خواهید شد و همه برنامهها و دادهها از جلسه مهمان کنونی حذف خواهند شد."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"به تعداد مجاز تعداد کاربر رسیدهاید"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">میتوانید حداکثر <xliff:g id="COUNT">%d</xliff:g> کاربر اضافه کنید.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"مرتبط کردن دستگاه جدید"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"برای ارسال محتوای این جلسه، لطفاً برنامه را باز کنید."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"برنامه ناشناس"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"توقف ارسال محتوا"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"همهفرتستی چطور کار میکند"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"همهفرستی"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"افرادی که در اطرافتان دستگاههای Bluetooth سازگار دارند میتوانند به رسانهای که همهفرستی میکنید گوش کنند"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 6b9f683dca30..85d0e205975a 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Kyllä, haluan jatkaa"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Lisätäänkö uusi käyttäjä?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kun lisäät uuden käyttäjän, hänen tulee määrittää oman tilansa asetukset.\n\nKaikki käyttäjät voivat päivittää sovelluksia muille käyttäjille."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Käyttäjäraja saavutettu"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Voit lisätä korkeintaan <xliff:g id="COUNT">%d</xliff:g> käyttäjää.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Muodosta uusi laitepari"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Jos haluat striimata tämän käyttökerran, avaa sovellus."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Tuntematon sovellus"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Lopeta striimaus"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Miten lähetys toimii"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Lähetys"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lähistöllä olevat ihmiset, joilla on yhteensopiva Bluetooth-laite, voivat kuunnella lähettämääsi mediaa"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index e90fa295459a..e9db78d1b664 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oui, continuer"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Ajouter un utilisateur?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Lorsque vous ajoutez un utilisateur, celui-ci doit configurer son espace.\n\nTout utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite d\'utilisateurs atteinte"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateur.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un autre appareil"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pour diffuser cette session, veuillez ouvrir l\'application."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Application inconnue"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement de la diffusion"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Diffusion"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité disposant d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index e21435383adb..95a351b99c1f 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oui, continuer"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Ajouter un utilisateur ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Lorsque vous ajoutez un utilisateur, celui-ci doit configurer son espace.\n\nN\'importe quel utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite nombre utilisateurs atteinte"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Vous pouvez ajouter <xliff:g id="COUNT">%d</xliff:g> profil utilisateur.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un nouvel appareil"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pour caster cette session, veuillez ouvrir l\'appli."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Appli inconnue"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Annonce"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 55a4d92adc97..a3dc2a288156 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Si, continuar"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Engadir un usuario novo?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Cando engadas un usuario novo, este deberá configurar o seu espazo.\n\nCalquera usuario pode actualizar as aplicacións para todos os demais usuarios."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Alcanzouse o límite de usuarios"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Podes engadir ata <xliff:g id="COUNT">%d</xliff:g> usuarios.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo novo"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para emitir esta sesión, abre a aplicación."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicación descoñecida"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Deter emisión"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funcionan as difusións?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Difusión"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As persoas que estean preto de ti e que dispoñan de dispositivos Bluetooth compatibles poden escoitar o contido multimedia que difundas"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 604a1eedb65e..300ac4d8881e 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ રાખવા માંગો છો?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"હા, ચાલુ રાખો"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"નવા વપરાશકર્તાને ઉમેરીએ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"જ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિને તેમનું સ્થાન સેટ કરવાની જરૂર પડે છે.\n\nકોઈપણ વપરાશકર્તા બધા અન્ય વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"વપરાશકર્તા સંખ્યાની મર્યાદા"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">તમે <xliff:g id="COUNT">%d</xliff:g> વપરાશકર્તા સુધી ઉમેરી શકો છો.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"આ સત્ર કાસ્ટ કરવા માટે, કૃપા કરીને ઍપ ખોલો."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"અજાણી ઍપ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"કાસ્ટ કરવાનું રોકો"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"બ્રોડકાસ્ટ પ્રક્રિયાની કામ કરવાની રીત"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"બ્રોડકાસ્ટ કરો"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"સુસંગત બ્લૂટૂથ ડિવાઇસ ધરાવતા નજીકના લોકો તમે જે મીડિયા બ્રોડકાસ્ટ કરી રહ્યાં છો તે સાંભળી શકે છે"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index bac2f5aed928..55ae4ea02561 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्या आप अपना सत्र जारी रखना चाहते हैं?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"हां, जारी रखें"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"नया उपयोगकर्ता जोड़ें?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"जब आप कोई नया उपयोगकर्ता जोड़ते हैं, तो उसे अपनी जगह सेट करनी होती है.\n\nकोई भी उपयोगकर्ता बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"अब और उपयोगकर्ता नहीं जोड़े जा सकते"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">आप ज़्यादा से ज़्यादा <xliff:g id="COUNT">%d</xliff:g> उपयोगकर्ता जोड़ सकते हैं.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नया डिवाइस जोड़ें"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्टिंग करना रोकें"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्ट करने की सुविधा कैसे काम करती है"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करें"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"आपके आस-पास मौजूद लोग, ब्रॉडकास्ट किए जा रहे मीडिया को सुन सकते हैं. हालांकि, इसके लिए उनके पास ऐसे ब्लूटूथ डिवाइस होने चाहिए जिन पर मीडिया चलाया जा सके"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 2d03d51356c5..01ced3631eb4 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -340,6 +340,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Način rada za goste"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Upotrebljavate način rada za goste"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Dodati novog korisnika?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kada dodate novog korisnika, ta osoba mora postaviti vlastiti prostor.\n\nBilo koji korisnik može ažurirati aplikacije za sve ostale korisnike."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ako dodate novog korisnika, napustit ćete način rada za goste i izbrisat će se svi podaci i aplikacije iz trenutačne gostujuće sesije."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dosegnuto je ograničenje korisnika"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item> @@ -840,7 +845,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Da biste emitirali ovu sesiju, otvorite aplikaciju."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi emitiranje"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako emitiranje funkcionira"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Emitiranje"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u blizini s kompatibilnim Bluetooth uređajima mogu slušati medije koje emitirate"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index a37c237f7a80..895b61e4f4d5 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Igen, folytatom"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Új felhasználó hozzáadása?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Ha új felhasználót ad hozzá, az illetőnek be kell állítania saját tárterületét.\n\nBármely felhasználó frissítheti az alkalmazásokat valamennyi felhasználó számára."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Maximális felhasználószám elérve"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Legfeljebb <xliff:g id="COUNT">%d</xliff:g> felhasználót adhat hozzá.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Új eszköz párosítása"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"A munkamenet átküldéséhez nyissa meg az alkalmazást."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ismeretlen alkalmazás"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Átküldés leállítása"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"A közvetítés működése"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Közvetítés"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"A közelben tartózkodó, kompatibilis Bluetooth-eszközzel rendelkező személyek meghallgathatják az Ön közvetített médiatartalmait"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index f724bd840225..c7024b5e02f8 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Շարունակե՞լ աշխատաշրջանը։"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Վերսկսել"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Այո, շարունակել"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Ավելացնե՞լ նոր օգտատեր"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Երբ նոր օգտատեր եք ավելացնում, նա պետք է կարգավորի իր պրոֆիլը:\n\nՑանկացած օգտատեր կարող է թարմացնել հավելվածները մյուս բոլոր հաշիվների համար:"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Սահմանաչափը սպառված է"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Հնարավոր է ավելացնել առավելագույնը <xliff:g id="COUNT">%d</xliff:g> օգտատեր։</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Նոր սարքի զուգակցում"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Այս աշխատաշրջանը հեռարձակելու համար բացեք հավելվածը"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Անհայտ հավելված"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Կանգնեցնել հեռարձակումը"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ինչպես է աշխատում հեռարձակումը"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Հեռարձակում"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ձեր մոտակայքում գտնվող՝ համատեղելի Bluetooth սարքերով մարդիկ կարող են լսել մեդիա ֆայլերը, որոնք դուք հեռարձակում եք։"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 2e879011f5f7..31979ed19455 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ya, lanjutkan"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Tambahkan pengguna baru?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Saat Anda menambahkan pengguna baru, orang tersebut perlu menyiapkan ruangnya sendiri.\n\nPengguna mana pun dapat mengupdate aplikasi untuk semua pengguna lain."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Batas pengguna tercapai"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Anda dapat menambahkan hingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sambungkan perangkat baru"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Buka aplikasi untuk mentransmisikan sesi ini."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikasi tidak dikenal"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hentikan transmisi"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara kerja siaran"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Siaran"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang di dekat Anda dengan perangkat Bluetooth yang kompatibel dapat mendengarkan media yang sedang Anda siarkan"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 80a694531ca0..e75a8f1d5c48 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Já, halda áfram"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Bæta nýjum notanda við?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Þegar þú bætir nýjum notanda við þarf sá notandi að setja upp svæðið sitt.\n\nHvaða notandi sem er getur uppfært forrit fyrir alla aðra notendur."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Notandahámarki náð"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Þú getur bætt við allt að <xliff:g id="COUNT">%d</xliff:g> notanda.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Para nýtt tæki"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Opnaðu forritið til að senda þessa lotu út."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Óþekkt forrit"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stöðva útsendingu"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Svona virkar útsending"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Útsending"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Fólk nálægt þér með samhæf Bluetooth-tæki getur hlustað á efnið sem þú sendir út"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index d2e0f0a92239..50741ee29c89 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sì, continua"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modalità Ospite"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Sei in modalità Ospite"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Aggiungere un nuovo utente?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Il nuovo utente, una volta aggiunto, deve impostare il proprio spazio.\n\nQualsiasi utente può aggiornare le app per tutti gli altri."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Se aggiungi un nuovo utente, la modalità Ospite viene disattivata e vengono eliminati tutti i dati e le app della sessione Ospite corrente."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite di utenti raggiunto"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Puoi aggiungere fino a <xliff:g id="COUNT">%d</xliff:g> utenti.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Accoppia nuovo dispositivo"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Per trasmettere questa sessione devi aprire l\'app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App sconosciuta"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Interrompi trasmissione"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Come funziona la trasmissione"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Annuncio"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Le persone vicine a te che hanno dispositivi Bluetooth compatibili possono ascoltare i contenuti multimediali che stai trasmettendo"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index e4d3dca7c7a8..451d96cc0792 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -342,6 +342,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"סשן חדש"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"כן, להמשיך"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"להוסיף משתמש חדש?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"כשמוסיפים משתמש חדש, המשתמש הזה צריך להגדיר את השטח שלו.\n\nכל משתמש יכול לעדכן אפליקציות עבור כל המשתמשים האחרים."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"הגעת למגבלת המשתמשים שניתן להוסיף"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="two">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item> @@ -847,7 +855,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"התאמה של מכשיר חדש"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"כדי להעביר (cast) את הסשן הזה, צריך לפתוח את האפליקציה."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"אפליקציה לא ידועה"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"עצירת ההעברה (casting)"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"הסבר על שידורים"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"שידור"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"אנשים בקרבת מקום עם מכשירי Bluetooth תואמים יכולים להאזין למדיה שמשודרת על ידך"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 170b31f91f6b..0cde116bf262 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"続行"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"新しいユーザーを追加しますか?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"新しいユーザーを追加したら、そのユーザーは自分のスペースをセットアップする必要があります。\n\nすべてのユーザーは他のユーザーに代わってアプリを更新できます。"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"ユーザー数が上限に達しました"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">最大 <xliff:g id="COUNT">%d</xliff:g> 人のユーザーを追加できます。</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"新しいデバイスとのペア設定"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"このセッションをキャストするには、アプリを開いてください。"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明なアプリ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"キャストを停止"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ブロードキャストの仕組み"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ブロードキャスト"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth 対応デバイスを持っている付近のユーザーは、あなたがブロードキャストしているメディアを聴けます"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index d675b7ce1fae..cc45a4a82f26 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"დიახ, გავაგრძელოთ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"სტუმრის რეჟიმი"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"თქვენ სტუმრის რეჟიმში ხართ"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"დაემატოს ახალი მომხმარებელი?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"ახალი მომხმარებლის დამატებისას, ამ მომხმარებელს საკუთარი სივრცის შექმნა მოუწევს.\n\nნებისმიერ მომხმარებელს შეუძლია აპები ყველა სხვა მომხმარებლისათვის განაახლოს."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"თუ ახალ მომხმარებელს დაამატებთ, სტუმრის რეჟიმი დაიხურება და სტუმრის რეჟიმის მიმდინარე სესიიდან ყველა აპი და მონაცემი წაიშლება."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"მიღწეულია მომხმარებელთა ლიმიტი"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">შესაძლებელია <xliff:g id="COUNT">%d</xliff:g>-მდე მომხმარებლის დამატება.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ახალი მოწყობილობის დაწყვილება"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ამ სესიის ტრანსლირებისთვის გახსენით აპი."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"უცნობი აპი"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ტრანსლირების შეწყვეტა"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ტრანსლირების მუშაობის პრინციპი"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ტრანსლაცია"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"თქვენთან ახლოს მყოფ ხალხს თავსებადი Bluetooth მოწყობილობით შეუძლიათ თქვენ მიერ ტრანსლირებული მედიის მოსმენა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 0538ddb2835c..9408df5a1078 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Иә, жалғастыру"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Жаңа пайдаланушы қосылсын ба?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Жаңа пайдаланушыны қосқанда, сол адам өз кеңістігін реттеуі керек.\n\nКез келген пайдаланушы барлық басқа пайдаланушылар үшін қолданбаларды жаңарта алады."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Пайдаланушылар саны шегіне жетті"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> пайдаланушыға дейін енгізуге болады.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңа құрылғымен жұптау"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Бұл сеансты трансляциялау үшін қолданбаны ашыңыз."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Белгісіз қолданба"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Трансляцияны тоқтату"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Тарату қалай жүзеге асады"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Тарату"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Үйлесімді Bluetooth құрылғылары бар маңайдағы адамдар сіз таратып жатқан медиамазмұнды тыңдай алады."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 37ba5183b06a..751f8e698bf3 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើអ្នកចង់បន្តវគ្គរបស់អ្នកទេ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើមសាជាថ្មី"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"បាទ/ចាស បន្ត"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"បញ្ចូលអ្នកប្រើថ្មីឬ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"នៅពេលអ្នកបញ្ចូលអ្នកប្រើថ្មី អ្នកប្រើនោះត្រូវរៀបចំកន្លែងរបស់គេ។\n\nអ្នកប្រើណាក៏អាចដំឡើងកំណែកម្មវិធីសម្រាប់អ្នកប្រើទាំងអស់ផ្សេងទៀតបានដែរ។"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"បានឈានដល់ចំនួនកំណត់អ្នកប្រើប្រាស់"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">អ្នកអាចបញ្ចូលអ្នកប្រើប្រាស់បានរហូតដល់ <xliff:g id="COUNT">%d</xliff:g> នាក់។</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ផ្គូផ្គងឧបករណ៍ថ្មី"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ដើម្បីភ្ជាប់វគ្គនេះ សូមបើកកម្មវិធី។"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"កម្មវិធីដែលមិនស្គាល់"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"បញ្ឈប់ការភ្ជាប់"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"របៀបដែលការផ្សាយដំណើរការ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ការផ្សាយ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"មនុស្សនៅជិតអ្នកដែលមានឧបករណ៍ប៊្លូធូសត្រូវគ្នាអាចស្តាប់មេឌៀដែលអ្នកកំពុងផ្សាយបាន"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index fa6e18eebe84..406fa1782e9e 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ಹೌದು, ಮುಂದುವರಿಸಿ"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸುವುದೇ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"ನೀವು ಒಬ್ಬ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿದಾಗ, ಆ ವ್ಯಕ್ತಿಯು ಅವರ ಸ್ಥಳವನ್ನು ಸ್ಥಾಪಿಸಬೇಕಾಗುತ್ತದೆ.\n\nಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"ಬಳಕೆದಾರರ ಮಿತಿ ತಲುಪಿದೆ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">ನೀವು <xliff:g id="COUNT">%d</xliff:g> ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ಈ ಸೆಶನ್ ಕಾಸ್ಟ್ ಮಾಡಲು, ಆ್ಯಪ್ ಅನ್ನು ತೆರೆಯಿರಿ."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ಅಪರಿಚಿತ ಆ್ಯಪ್"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ಬಿತ್ತರಿಸುವುದನ್ನು ನಿಲ್ಲಿಸಿ"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ಪ್ರಸಾರವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ಪ್ರಸಾರ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ಹೊಂದಾಣಿಕೆಯಾಗುವ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ಹೊಂದಿರುವ ಸಮೀಪದಲ್ಲಿರುವ ಜನರು ನೀವು ಪ್ರಸಾರ ಮಾಡುತ್ತಿರುವ ಮಾಧ್ಯಮವನ್ನು ಆಲಿಸಬಹುದು"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index e4e4dbf2b905..60bb93302dff 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"계속 진행"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"신규 사용자를 추가할까요?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"추가된 새로운 사용자는 자신의 공간을 설정해야 합니다.\n\n사용자라면 누구든 다른 사용자를 위해 앱을 업데이트할 수 있습니다."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"사용자 제한 도달"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">사용자를 <xliff:g id="COUNT">%d</xliff:g>명까지 추가할 수 있습니다.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"새 기기와 페어링"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"세션을 전송하려면 앱을 열어 주세요"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"알 수 없는 앱"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"전송 중지"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"브로드캐스팅 작동 원리"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"브로드캐스트"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"호환되는 블루투스 기기를 가진 근처의 사용자가 내가 브로드캐스트 중인 미디어를 수신 대기할 수 있습니다."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index be9db2efd1ac..784e5835bb7f 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ооба, уланта берели"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Конок режими"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Конок режиминдесиз"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Жаңы колдонуучу кошосузбу?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Жаңы колдонуучу кошулганда, ал өз мейкиндигин түзүп алышы керек.\n\nКолдонмолорду бир колдонуучу жаңыртканда, ал калган бардык колдонуучулар үчүн да жаңырат."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Жаңы колдонуучуну кошсоңуз, конок режими жабылат жана учурдагы конок сеансындагы бардык колдонмолор жана дайындар өчүрүлөт."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Дагы колдонуучу кошууга болбойт"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> колдонуучуга чейин кошууга болот.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңы түзмөк кошуу"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Бул сеансты тышкы экранга чыгаруу үчүн колдонмону ачыңыз."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Белгисиз колдонмо"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Тышкы экранга чыгарууну токтотуу"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Кабарлоо кантип иштейт"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Кабарлоо"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Шайкеш Bluetooth түзмөктөрү болгон жакын жердеги кишилер кабарлап жаткан медиаңызды уга алышат"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 50b3215161f9..5df0e6c8fd00 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານຕ້ອງການສືບຕໍ່ເຊດຊັນຂອງທ່ານບໍ່?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ຕົກລົງ, ດຳເນີນການຕໍ່"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ໂໝດແຂກ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ທ່ານກຳລັງຢູ່ໃນໂໝດແຂກ"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"ເພີ່ມຜູ້ໃຊ້ໃໝ່ບໍ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"ເມື່ອທ່ານເພີ່ມຜູ້ໃຊ້ໃໝ່, ຜູ້ໃຊ້ນັ້ນຈະຕ້ອງຕັ້ງຄ່າພື້ນທີ່ບ່ອນຈັດເກັບຂໍ້ມູນຂອງລາວ.\n\nຜູ້ໃຊ້ທຸກຄົນສາມາດອັບເດດແອັບຂອງຜູ້ໃຊ້ຄົນອື່ນທັງໝົດໄດ້."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ການເພີ່ມຜູ້ໃຊ້ໃໝ່ຈະອອກຈາກໂໝດແຂກ ແລະ ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດອອກຈາກໄລຍະເວລາຂອງແຂກປັດຈຸບັນ."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ຮອດຂີດຈຳກັດຜູ້ໃຊ້ແລ້ວ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">ທ່ານສາມາດເພີ່ມໄດ້ສູງສຸດ <xliff:g id="COUNT">%d</xliff:g> ຄົນ.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ເພື່ອສົ່ງສັນຍານເຊດຊັນນີ້, ກະລຸນາເປີດແອັບ."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ແອັບທີ່ບໍ່ຮູ້ຈັກ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ຢຸດການສົ່ງສັນຍານ"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ການອອກອາກາດເຮັດວຽກແນວໃດ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ອອກອາກາດ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ຄົນທີ່ຢູ່ໃກ້ທ່ານທີ່ມີອຸປະກອນ Bluetooth ທີ່ເຂົ້າກັນໄດ້ຈະສາມາດຟັງມີເດຍທີ່ທ່ານກຳລັງອອກອາກາດຢູ່ໄດ້"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 667a666cc3e0..ab99ddea8957 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -342,6 +342,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Taip, tęsti"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Pridėti naują naudotoją?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kai pridedate naują naudotoją, šis asmuo turi nustatyti savo erdvę.\n\nBet kuris naudotojas gali atnaujinti visų kitų naudotojų programas."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Pasiekta naudotojų riba"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojo.</item> @@ -847,7 +855,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Naujo įrenginio susiejimas"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Jei norite perduoti šį seansą, atidarykite programą."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nežinoma programa"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sustabdyti perdavimą"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kaip veikia transliacija"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transliacija"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Netoliese esantys žmonės, turintys suderinamus „Bluetooth“ įrenginius, gali klausyti jūsų transliuojamos medijos"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 516bcabd4843..e22c1bb5490e 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -340,6 +340,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Jā, turpināt"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Vai pievienot jaunu lietotāju?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kad pievienosiet jaunu lietotāju, viņam būs jāizveido savs profils.\n\nIkviens lietotājs var atjaunināt lietotnes citu lietotāju vietā."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Sasniegts lietotāju ierobežojums"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="zero">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotājus.</item> @@ -841,7 +849,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Savienošana pārī ar jaunu ierīci"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Lai apraidītu šo sesiju, lūdzu, atveriet lietotni."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nezināma lietotne"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Apturēt apraidi"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kā darbojas apraide"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Apraide"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Tuvumā esošās personas ar saderīgām Bluetooth ierīcēm var klausīties jūsu apraidīto multivides saturu."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 360966342c29..54974bfa453e 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продолжи"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Да се додаде нов корисник?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Кога додавате нов корисник, тоа лице треба да го постави својот простор.\n\nСекој корисник може да ажурира апликации за сите други корисници."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнато ограничување на корисник"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Може да додадете најмногу <xliff:g id="COUNT">%d</xliff:g> корисник.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спарете нов уред"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"За да ја емитувате сесијава, отворете ја апликацијата."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Непозната апликација"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Сопри со емитување"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционира емитувањето"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Емитување"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Луѓето во ваша близина со компатибилни уреди со Bluetooth може да ги слушаат аудиозаписите што ги емитувате"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 31d33d7d991f..997603ad02d0 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"അതെ, തുടരുക"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"അതിഥി മോഡ്"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"നിങ്ങൾ അതിഥി മോഡിലാണ്"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"പുതിയ ഉപയോക്താവിനെ ചേർക്കണോ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"നിങ്ങൾ പുതിയൊരു ഉപയോക്താവിനെ ചേർക്കുമ്പോൾ, ആ വ്യക്തിക്ക് അവരുടെ ഇടം സജ്ജീകരിക്കേണ്ടതുണ്ട്.\n\nമറ്റ് എല്ലാ ഉപയോക്താക്കൾക്കുമായി ഏതൊരു ഉപയോക്താവിനും ആപ്പുകൾ അപ്ഡേറ്റ് ചെയ്യാനാവും."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"പുതിയൊരു ഉപയോക്താവിനെ ചേർത്താൽ അതിഥി മോഡിൽ നിന്ന് പുറത്ത് കടക്കുകയും നിലവിലെ അതിഥി മോഡിലുള്ള എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കുകയും ചെയ്യും."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ഉപയോക്തൃ പരിധി എത്തി"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">നിങ്ങൾക്ക് <xliff:g id="COUNT">%d</xliff:g> ഉപയോക്താക്കളെ വരെ ചേർക്കാനാവും.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"പുതിയ ഉപകരണവുമായി ജോടിയാക്കുക"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ഈ സെഷൻ കാസ്റ്റ് ചെയ്യാൻ, ആപ്പ് തുറക്കുക."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"അജ്ഞാതമായ ആപ്പ്"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"കാസ്റ്റ് ചെയ്യുന്നത് നിർത്തുക"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ബ്രോഡ്കാസ്റ്റ് എങ്ങനെയാണ് പ്രവർത്തിക്കുന്നത്"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ബ്രോഡ്കാസ്റ്റ്"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"അനുയോജ്യമായ Bluetooth ഉപകരണങ്ങളോടെ സമീപമുള്ള ആളുകൾക്ക് നിങ്ങൾ ബ്രോഡ്കാസ്റ്റ് ചെയ്യുന്ന മീഡിയ കേൾക്കാനാകും"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 2f3a089b3895..64e6271dc7f8 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Тийм, үргэлжлүүлэх"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Зочны горим"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Та зочны горимд байна"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Шинэ хэрэглэгч нэмэх үү?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Та шинэ хэрэглэгч нэмбэл тухайн хүн өөрийн профайлыг тохируулах шаардлагатай.\n\nАль ч хэрэглэгч бүх хэрэглэгчийн аппуудыг шинэчлэх боломжтой."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Шинэ хэрэглэгч нэмснээр зочны горимоос гаргах бөгөөд бүх апп болон өгөгдлийг одоогийн зочны харилцан үйлдлээс устгана."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Хэрэглэгчийн хязгаарт хүрсэн"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Та <xliff:g id="COUNT">%d</xliff:g> хүртэлх хэрэглэгч нэмэх боломжтой.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Шинэ төхөөрөмж хослуулах"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Энэ үйл явдлыг дамжуулахын тулд аппыг нээнэ үү."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Үл мэдэгдэх апп"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Дамжуулахыг зогсоох"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Нэвтрүүлэлт хэрхэн ажилладаг вэ?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Нэвтрүүлэлт"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Тохиромжтой Bluetooth төхөөрөмжүүдтэй таны ойролцоох хүмүүс таны нэвтрүүлж буй медиаг сонсох боломжтой"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 9e2b73e1faed..c710fb39f286 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"होय, सुरू ठेवा"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"अतिथी मोड"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"तुम्ही अतिथी मोडमध्ये आहात"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"नवीन वापरकर्ता जोडायचा?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"तुम्ही एक नवीन वापरकर्ता जोडता तेव्हा, त्या व्यक्तीने त्यांचे स्थान सेट करणे आवश्यक असते.\n\nकोणताही वापरकर्ता इतर सर्व वापरकर्त्यांसाठी अॅप्स अपडेट करू शकतो."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"नवीन वापरकर्ता जोडल्याने अतिथी मोडमधून बाहेर पडेल आणि सध्याच्या अतिथी सत्रातील सर्व अॅप्स व डेटा हटवला जाईल."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"वापरकर्ता मर्यादा गाठली"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">तुम्ही <xliff:g id="COUNT">%d</xliff:g> वापरकर्त्यांपर्यंत जोडू शकता.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नवीन डिव्हाइससोबत पेअर करा"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"हे सेशन कास्ट करण्यासाठी, कृपया ॲप उघडा."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अज्ञात अॅप"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करणे थांबवा"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्टिंग कसे काम करते"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करा"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कंपॅटिबिल ब्लूटूथ डिव्हाइस असलेले तुमच्या जवळपासचे लोक हे तुम्ही ब्रॉडकास्ट करत असलेला मीडिया ऐकू शकतात"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 046025e40237..19236e0fd727 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ya, teruskan"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mod tetamu"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Anda dalam mod tetamu"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Tambah pengguna baharu?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Apabila anda menambah pengguna baharu, orang itu perlu menyediakan ruang mereka.\n\nMana-mana pengguna boleh mengemas kini apl untuk semua pengguna lain."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Tindakan menambahkan pengguna baharu akan menyebabkan anda keluar daripada mod tetamu dan memadamkan semua apl dan data daripada sesi tetamu semasa."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Had pengguna dicapai"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Anda boleh menambah sehingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Gandingkan peranti baharu"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Untuk menghantar sesi ini, sila buka apl."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Apl yang tidak diketahui"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Berhenti menghantar"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara siaran berfungsi"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Siarkan"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang berdekatan anda dengan peranti Bluetooth yang serasi boleh mendengar media yang sedang anda siarkan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 00079fc5215b..08c3f4095bad 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်၏ စက်ရှင်ကို ဆက်လုပ်လိုပါသလား။"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ပြန်စပါ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ဆက်လုပ်ပါ"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"အသုံးပြုသူအသစ်ကို ထည့်မလား။"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"သင်ထည့်လိုက်သော အသုံးပြုသူအသစ်သည် ၎င်း၏နေရာကို သတ်မှတ်စီစဉ်ရန် လိုအပ်သည်။\n\nမည်သည့်အသုံးပြုသူမဆို ကျန်သူများအားလုံးအတွက် အက်ပ်များကို အပ်ဒိတ်လုပ်ပေးနိုင်သည်။"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"အသုံးပြုသူ အကန့်အသတ် ပြည့်သွားပါပြီ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">အသုံးပြုသူ <xliff:g id="COUNT">%d</xliff:g> ဦးအထိ ထည့်နိုင်သည်။</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"စက်အသစ် တွဲချိတ်ရန်"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"အက်ပ်ဖွင့်ပြီး ဤစက်ရှင်ကို ကာစ်လုပ်နိုင်သည်။"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"အမည်မသိ အက်ပ်"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ကာစ် ရပ်ရန်"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ထုတ်လွှင့်မှုဆောင်ရွက်ပုံ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ထုတ်လွှင့်ခြင်း"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"အနီးရှိတွဲသုံးနိုင်သော ဘလူးတုသ်သုံးစက် အသုံးပြုသူများက သင်ထုတ်လွှင့်နေသော မီဒီယာကို နားဆင်နိုင်သည်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 37f0a5307520..bccc9e025aac 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsett"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Vil du legge til en ny bruker?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Når du legger til en ny bruker, må vedkommende konfigurere sitt eget område.\n\nAlle brukere kan oppdatere apper for alle andre brukere."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Grensen for antall brukere er nådd"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Du kan legge til opptil <xliff:g id="COUNT">%d</xliff:g> brukere.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Koble til en ny enhet"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"For å caste denne økten, åpne appen."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ukjent app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stopp castingen"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Slik fungerer kringkasting"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Kringkasting"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Folk i nærheten med kompatible Bluetooth-enheter kan lytte til mediene du kringkaster"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index cc025b7335a3..9d8958472b87 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"हो, जारी राख्नुहोस्"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"नयाँ प्रयोगकर्ता थप्ने हो?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"जब तपाईँले नयाँ प्रयोगकर्ता थप्नुहुन्छ, त्यस प्रयोगकर्ताले आफ्नो स्थान स्थापना गर्न पर्ने छ।\n\nसबै प्रयोगकर्ताले अरू प्रयोगकर्ताका एपहरू अपडेट गर्न सक्छन्।"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"प्रयोगकर्ताको सीमा पुग्यो"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">तपाईं अधिकतम <xliff:g id="COUNT">%d</xliff:g> प्रयोगहरू मात्र थप्न सक्नुहुन्छ।</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"यो सत्र कास्ट गर्न चाहनुहुन्छ भने कृपया एप खोल्नुहोस्।"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अज्ञात एप"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट गर्न छाड्नुहोस्"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"प्रसारण गर्ने सुविधाले कसरी काम गर्छ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"प्रसारण"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कम्प्याटिबल ब्लुटुथ डिभाइस भएका नजिकैका मान्छेहरू तपाईंले प्रसारण गरिरहनुभएको मिडिया सुन्न सक्छन्"</string> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index dc2bee56373c..54c5490d9fe6 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -66,12 +66,10 @@ <!-- media output dialog--> <color name="media_dialog_background">@color/material_dynamic_neutral10</color> - <color name="media_dialog_item_main_content">@color/material_dynamic_primary90</color> - <color name="media_dialog_item_background">@color/material_dynamic_neutral_variant20</color> - <color name="media_dialog_connected_item_background">@color/material_dynamic_secondary20</color> - <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color> - <color name="media_dialog_button_background">@color/material_dynamic_primary70</color> - <color name="media_dialog_solid_button_text">@color/material_dynamic_secondary20</color> + <color name="media_dialog_active_item_main_content">@color/material_dynamic_neutral10</color> + <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_neutral10</color> + <color name="media_dialog_item_status">@color/material_dynamic_neutral10</color> + <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color> <!-- Biometric dialog colors --> <color name="biometric_dialog_gray">#ffcccccc</color> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 8e545296f7b4..764ca8ca9821 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, doorgaan"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Nieuwe gebruiker toevoegen?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Als je een nieuwe gebruiker toevoegt, moet die persoon zijn eigen profiel instellen.\n\nElke gebruiker kan apps updaten voor alle andere gebruikers."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Gebruikerslimiet bereikt"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Je kunt maximaal <xliff:g id="COUNT">%d</xliff:g> gebruikers toevoegen.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Nieuw apparaat koppelen"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Als je deze sessie wilt casten, open je de app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Casten stoppen"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitzenden werkt"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Uitzending"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mensen bij jou in de buurt met geschikte bluetooth-apparaten kunnen luisteren naar de media die je uitzendt"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 258e9a1b8ea7..9894d70eeb54 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ସେସନ୍ ଜାରି ରଖିବାକୁ ଚାହାଁନ୍ତି କି?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ହଁ, ଜାରି ରଖନ୍ତୁ"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"ନୂତନ ଉପଯୋଗକର୍ତ୍ତା ଯୋଗ କରିବେ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"ଆପଣ ଜଣେ ନୂଆ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କଲେ,ତାଙ୍କୁ ସ୍ପେସ୍ ସେଟ୍ ଅପ୍ କରିବାକୁ ପଡ଼ିବ। \n \n ଅନ୍ୟ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଯେ କୌଣସି ଉପଯୋଗକର୍ତ୍ତା ଆପଗୁଡ଼ିକୁ ଅପଡେଟ୍ କରିପାରିବେ।"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"ଉପଯୋଗକର୍ତ୍ତା ସୀମାରେ ପହଞ୍ଚିଛି"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">କେବଳ <xliff:g id="COUNT">%d</xliff:g> ଉପଯୋଗକର୍ତ୍ତା ହିଁ ତିଆରି କରିହେବ।</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ନୂଆ ଡିଭାଇସକୁ ପେୟାର୍ କରନ୍ତୁ"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ଏହି ସେସନକୁ କାଷ୍ଟ କରିବା ପାଇଁ, ଦୟାକରି ଆପ ଖୋଲନ୍ତୁ।"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ଅଜଣା ଆପ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରନ୍ତୁ"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ବ୍ରଡକାଷ୍ଟିଂ କିପରି କାମ କରେ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ବ୍ରଡକାଷ୍ଟ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ଆପଣଙ୍କ ଆଖପାଖର କମ୍ପାଟିବଲ ବ୍ଲୁଟୁଥ ଡିଭାଇସ ଥିବା ଲୋକମାନେ ଆପଣ ବ୍ରଡକାଷ୍ଟ କରୁଥିବା ମିଡିଆ ଶୁଣିପାରିବେ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index d06fd267abda..ed4a0c0ef5f2 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ਹਾਂ, ਜਾਰੀ ਰੱਖੋ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ਮਹਿਮਾਨ ਮੋਡ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ਤੁਸੀਂ ਮਹਿਮਾਨ ਮੋਡ ਵਿੱਚ ਹੋ"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"ਕੀ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਨਾ ਹੈ?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਦੇ ਹੋ, ਉਸ ਵਿਅਕਤੀ ਨੂੰ ਆਪਣੀ ਜਗ੍ਹਾ ਸਥਾਪਤ ਕਰਨ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ।\n\nਕੋਈ ਵੀ ਵਰਤੋਂਕਾਰ ਹੋਰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਸਕਦਾ ਹੈ।"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ਨਵੇਂ ਵਰਤੋਂਕਾਰ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਨਾਲ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਮੋਡ ਚਲਾ ਜਾਵੇਗਾ ਅਤੇ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ।"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਸੀਮਾ ਪੂਰੀ ਹੋਈ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">ਤੁਸੀਂ <xliff:g id="COUNT">%d</xliff:g> ਤੱਕ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ਇਸ ਸੈਸ਼ਨ ਨੂੰ ਕਾਸਟ ਕਰਨ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਖੋਲ੍ਹੋ।"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ਅਗਿਆਤ ਐਪ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ਪ੍ਰਸਾਰਨ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ਪ੍ਰਸਾਰਨ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ਅਨੁਰੂਪ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਨਾਲ ਨਜ਼ਦੀਕੀ ਲੋਕ ਤੁਹਾਡੇ ਵੱਲੋਂ ਪ੍ਰਸਾਰਨ ਕੀਤੇ ਜਾ ਰਹੇ ਮੀਡੀਆ ਨੂੰ ਸੁਣ ਸਕਦੇ ਹਨ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index c9e8a297bf8c..ae99202c7f0d 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -342,6 +342,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Tak, kontynuuj"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Tryb gościa"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Jesteś w trybie gościa"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Dodać nowego użytkownika?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Gdy dodasz nowego użytkownika, musi on skonfigurować swój profil.\n\nKażdy użytkownik może aktualizować aplikacje wszystkich innych użytkowników."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodanie nowego użytkownika spowoduje zamknięcie trybu gościa. Wszystkie aplikacje i dane z obecnej sesji gościa zostaną usunięte."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Osiągnięto limit użytkowników"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkowników.</item> @@ -846,7 +851,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sparuj nowe urządzenie"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Aby przesłać tę sesję, otwórz aplikację."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nieznana aplikacja"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zatrzymaj przesyłanie"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak działa transmitowanie"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisja"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osoby w pobliżu ze zgodnymi urządzeniami Bluetooth mogą słuchać transmitowanych przez Ciebie multimediów"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index d2f246c6568b..3bb02ab00045 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Adicionar novo usuário?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Quando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para os demais usuários."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de usuários atingido"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Abra o app para transmitir esta sessão."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecido"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index d8519df6d3a7..2e7e2cb96c2e 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Adicionar um novo utilizador?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Ao adicionar um novo utilizador, essa pessoa tem de configurar o respetivo espaço.\n\nQualquer utilizador pode atualizar apps para todos os outros utilizadores."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de utilizadores alcançado"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Pode adicionar até <xliff:g id="COUNT">%d</xliff:g> utilizadores.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sincronize o novo dispositivo"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para transmitir esta sessão, abra a app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecida"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmissão"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas de si com dispositivos Bluetooth compatíveis podem ouvir o conteúdo multimédia que está a transmitir"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index d2f246c6568b..3bb02ab00045 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Adicionar novo usuário?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Quando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para os demais usuários."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de usuários atingido"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Abra o app para transmitir esta sessão."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecido"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index d7d66220f079..7203d972aa48 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -340,6 +340,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, continuați"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modul pentru invitați"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Folosiți modul pentru invitați"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Adăugați un utilizator nou?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Când adăugați un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOrice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dacă adăugați un utilizator nou, veți ieși din modul pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Ați atins limita de utilizatori"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Puteți adăuga maximum <xliff:g id="COUNT">%d</xliff:g> utilizatori.</item> @@ -840,7 +845,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Asociați un nou dispozitiv"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pentru a proiecta această sesiune, deschideți aplicația."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicație necunoscută"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Nu mai proiectați"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cum funcționează transmisia"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmiteți"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Persoanele din apropiere cu dispozitive Bluetooth compatibile pot asculta conținutul pe care îl transmiteți"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 427c4bee4a95..1af2ebece172 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -342,6 +342,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продолжить"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Добавить пользователя?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Когда вы добавите пользователя, ему потребуется настроить профиль.\n\nЛюбой пользователь устройства может обновлять приложения для всех аккаунтов."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнут лимит"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователя.</item> @@ -847,7 +855,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Подключить новое устройство"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Чтобы начать трансляцию сеанса, откройте приложение"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Неизвестное приложение"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Остановить трансляцию"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работают трансляции"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляция"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Находящиеся рядом с вами люди с совместимыми устройствами Bluetooth могут слушать медиафайлы, которые вы транслируете."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index d9cefbff5051..e01b14392834 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්යද?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ඔව්, දිගටම කරගෙන යන්න"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"අලුත් පරිශීලකයෙක් එක් කරන්නද?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"ඔබ අලුත් පරිශීලකයෙක් එකතු කරන විට, එම පුද්ගලයා ඔහුගේ වැඩ කරන ඉඩ සකසා ගත යුතුය.\n\nසියළුම අනෙක් පරිශීලකයින් සඳහා ඕනෑම පරිශීලකයෙකුට යාවත්කාලීන කළ හැක."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"පරිශීලක සීමාවට ළඟා විය"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">ඔබට පරිශීලකයින් <xliff:g id="COUNT">%d</xliff:g>ක් දක්වා එක් කළ හැකිය.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"නව උපාංගය යුගල කරන්න"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"මෙම සැසිය විකාශය කිරීමට, කරුණාකර යෙදුම විවෘත කරන්න."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"නොදන්නා යෙදුම"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"විකාශය නවතන්න"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"විකාශනය ක්රියා කරන ආකාරය"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"විකාශනය"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ගැළපෙන බ්ලූටූත් උපාංග සහිත ඔබ අවට සිටින පුද්ගලයින්ට ඔබ විකාශනය කරන මාධ්යයට සවන් දිය හැකිය"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 22f0a0499ecd..2d827770801f 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -342,6 +342,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Áno, pokračovať"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim pre hostí"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Ste v režime pre hostí"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Pridať nového používateľa?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Keď pridáte nového používateľa, musí si nastaviť vlastný priestor.\n\nKtorýkoľvek používateľ môže aktualizovať aplikácie všetkých ostatných používateľov."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ak pridáte nového používatelia, ukončí sa režim pre hostí a odstránia sa všetky aplikácie a údaje z aktuálnej relácie hosťa."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dosiahnutý limit počtu používateľov"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Môžete pridať maximálne <xliff:g id="COUNT">%d</xliff:g> používateľov.</item> @@ -846,7 +851,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovať nové zariadenie"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Ak chcete túto reláciu prenášať, otvorte aplikáciu."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Neznáma aplikácia"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zastaviť prenos"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ako vysielanie funguje"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Vysielanie"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ľudia v okolí s kompatibilnými zariadeniami s rozhraním Bluetooth si môžu vypočuť médiá, ktoré vysielate"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 5b06c838278e..08468300409d 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -342,6 +342,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nadaljuj"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Način za goste"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Ste v načinu za goste"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Dodajanje novega uporabnika?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Ko dodate novega uporabnika, mora ta nastaviti svoj prostor.\n\nVsak uporabnik lahko posodobi aplikacije za vse druge uporabnike."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Če dodate novega uporabnika, se bo način za goste zaprl, aplikacije in podatki v trenutni seji gosta pa bodo izbrisani."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Omejitev uporabnikov je dosežena"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnika.</item> @@ -846,7 +851,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Seznanitev nove naprave"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Če želite predvajati to sejo, odprite aplikacijo."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Neznana aplikacija"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ustavi predvajanje"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako deluje oddajanje"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Oddajanje"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osebe v bližini z združljivo napravo Bluetooth lahko poslušajo predstavnost, ki jo oddajate."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 3ddea24826e7..bacc6d22b1d1 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Të shtohet përdorues i ri?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kur shton një përdorues të ri, ai person duhet të konfigurojë hapësirën e vet.\n\nÇdo përdorues mund t\'i përditësojë aplikacionet për të gjithë përdoruesit e tjerë."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"U arrit kufiri i përdoruesve"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Mund të shtosh deri në <xliff:g id="COUNT">%d</xliff:g> përdorues.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Çifto pajisjen e re"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Hap aplikacionin për të transmetuar këtë seancë."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikacion i panjohur"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ndalo transmetimin"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Si funksionon transmetimi"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmetimi"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personat në afërsi me ty me pajisje të përputhshme me Bluetooth mund të dëgjojnë median që ti po transmeton"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 4f4ffabe545a..a18ec92404d7 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -340,6 +340,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, настави"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим госта"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Користите режим госта"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Додајете новог корисника?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Када додате новог корисника, та особа треба да подеси свој простор.\n\nСваки корисник може да ажурира апликације за све остале кориснике."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Додавањем новог корисника изаћи ћете из режима госта и избрисаћете све апликације и податке из актуелне сесије госта."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнут максимални број корисника"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item> @@ -840,7 +845,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Упари нови уређај"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Да бисте пребацивали ову сесију, отворите апликацију."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Непозната апликација"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Заустави пребацивање"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционише емитовање"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Емитовање"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Људи у близини са компатибилним Bluetooth уређајима могу да слушају медијски садржај који емитујете"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index caed13246015..521cdbbbeabc 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsätt"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Lägga till ny användare?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"När du lägger till en ny användare måste den personen konfigurera sitt utrymme.\n\nAlla användare kan uppdatera appar för samtliga användares räkning."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Användargränsen har nåtts"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Det går att lägga till upp till <xliff:g id="COUNT">%d</xliff:g> användare.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parkoppla en ny enhet"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Öppna appen om du vill casta den här sessionen."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Okänd app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sluta casta"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Så fungerar utsändning"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Utsändning"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i närheten med kompatibla Bluetooth-enheter kan lyssna på medieinnehåll som du sänder ut"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 6d13e158ba7a..a4f7bd650d47 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza upya"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ndiyo, endelea"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Ungependa kuongeza mtumiaji?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Mtumiaji mpya utakayemwongeza atahitaji kuongeza akaunti yake.\n\nMtumiaji yoyote anaweza kusasisha programu kwa niaba ya wengine wote."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Umefikia kima cha juu cha watumiaji"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Unaruhusiwa kuongeza hadi watumiaji <xliff:g id="COUNT">%d</xliff:g>.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Oanisha kifaa kipya"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Ili utume kipindi hiki, tafadhali fungua programu."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Programu isiyojulikana"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Acha kutuma"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jinsi utangazaji unavyofanya kazi"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Tangaza"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Watu walio karibu nawe wenye vifaa oanifu vya Bluetooth wanaweza kusikiliza maudhui unayoyatangaza"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index cbdc9e027634..6f53de26e87e 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"தொடரவும்"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"கெஸ்ட் பயன்முறை"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"கெஸ்ட் பயன்முறையில் உள்ளீர்கள்"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"புதியவரைச் சேர்க்கவா?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"புதிய பயனரைச் சேர்க்கும்போது, அவர் தனக்கான இடத்தை அமைக்க வேண்டும்.\n\nஎந்தவொரு பயனரும், மற்ற எல்லா பயனர்களுக்காகவும் ஆப்ஸைப் புதுப்பிக்கலாம்."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"புதிய பயனரைச் சேர்த்தால் கெஸ்ட் பயன்முறையில் இருந்து வெளியேற்றப்படுவீர்கள். மேலும் தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"பயனர் வரம்பை அடைந்துவிட்டீர்கள்"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> பயனர்கள் வரை சேர்க்க முடியும்.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"புதிய சாதனத்தை இணைத்தல்"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"இந்த அமர்வை அலைபரப்ப ஆப்ஸைத் திறங்கள்."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"அறியப்படாத ஆப்ஸ்"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"அலைபரப்புவதை நிறுத்து"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"பிராட்காஸ்ட் எவ்வாறு செயல்படுகிறது?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"பிராட்காஸ்ட்"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"நீங்கள் பிராட்காஸ்ட் செய்யும் மீடியாவை அருகிலுள்ளவர்கள் இணக்கமான புளூடூத் சாதனங்கள் மூலம் கேட்கலாம்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 08e9c2fcc99a..87d9e952285a 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"అవును, కొనసాగించు"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"గెస్ట్ మోడ్"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"మీరు గెస్ట్ మోడ్లో ఉన్నారు"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"కొత్త యూజర్ను జోడించాలా?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"ఒక కొత్త యూజర్ను మీరు జోడించినప్పుడు, ఆ వ్యక్తి తన స్పేస్ను సెటప్ చేసుకోవాలి.\n\nఏ యూజర్ అయినా మిగతా అందరు యూజర్ల కోసం యాప్లను అప్డేట్ చేయగలరు."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"కొత్త యూజర్ను జోడించడం వలన గెస్ట్ మోడ్ నుండి నిష్క్రమించబడుతుంది, ప్రస్తుత గెస్ట్ సెషన్ నుండి అన్ని యాప్లు, డేటా తొలగించండి."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"వినియోగదారు పరిమితిని చేరుకున్నారు"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">మీరు <xliff:g id="COUNT">%d</xliff:g> వినియోగదారుల వరకు జోడించవచ్చు.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ఈ సెషన్ను ప్రసారం చేయడానికి, దయచేసి యాప్ను తెరవండి."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"తెలియని యాప్"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ప్రసారాన్ని ఆపివేయండి"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ప్రసారం కావడం అనేది ఎలా పని చేస్తుంది"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ప్రసారం"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"మీకు సమీపంలో ఉన్న వ్యక్తులు అనుకూలత ఉన్న బ్లూటూత్ పరికరాలతో మీరు ప్రసారం చేస్తున్న మీడియాను వినగలరు"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 5db1ee3bd404..8ddd9da5a504 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ใช่ ดำเนินการต่อ"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"ต้องการเพิ่มผู้ใช้ใหม่ใช่ไหม"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"เมื่อคุณเพิ่มผู้ใช้ใหม่ ผู้ใช้ดังกล่าวจะต้องตั้งค่าพื้นที่ของตนเอง\n\nผู้ใช้ทุกคนสามารถอัปเดตแอปสำหรับผู้ใช้รายอื่นทุกคนได้"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"ถึงขีดจำกัดผู้ใช้แล้ว"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">คุณเพิ่มผู้ใช้ได้สูงสุด <xliff:g id="COUNT">%d</xliff:g> คน</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"จับคู่อุปกรณ์ใหม่"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"โปรดเปิดแอปหากต้องการแคสต์เซสชันนี้"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"แอปที่ไม่รู้จัก"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"หยุดแคสต์"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"วิธีการทำงานของการออกอากาศ"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"ประกาศ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ผู้ที่อยู่ใกล้คุณและมีอุปกรณ์บลูทูธที่รองรับสามารถรับฟังสื่อที่คุณกำลังออกอากาศได้"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index e4650b72e3b7..85efeda17f4d 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oo, magpatuloy"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Magdagdag ng bagong user?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Kapag nagdagdag ka ng bagong user, kailangang i-set up ng taong iyon ang kanyang espasyo.\n\nAng sinumang user ay maaaring mag-update ng mga app para sa lahat ng iba pang user."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Naabot na ang limitasyon sa user"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Maaari kang magdagdag ng hanggang <xliff:g id="COUNT">%d</xliff:g> user.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Magpares ng bagong device"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para ma-cast ang session na ito, buksan ang app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Hindi kilalang app"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ihinto ang pag-cast"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Paano gumagana ang pag-broadcast"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Makakapakinig ang mga taong malapit sa iyo na may mga compatible na Bluetooth device sa media na bino-broadcast mo"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 942416277779..162442474db1 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Evet, devam et"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Yeni kullanıcı eklensin mi?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Yeni bir kullanıcı eklediğinizde, bu kişinin kendi alanını ayarlaması gerekir.\n\nHerhangi bir kullanıcı, diğer tüm kullanıcılar için uygulamaları güncelleyebilir."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Kullanıcı sınırına ulaşıldı"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">En fazla <xliff:g id="COUNT">%d</xliff:g> kullanıcı ekleyebilirsiniz.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihaz eşle"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Bu oturumu yayınlamak için lütfen uygulamayı açın."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Bilinmeyen uygulama"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayını durdur"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayınlamanın işleyiş şekli"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Anons"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Yakınınızda ve uyumlu Bluetooth cihazları olan kişiler yayınladığınız medya içeriğini dinleyebilir"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index c928972e3458..aeaec2b29777 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -342,6 +342,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Так, продовжити"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Додати нового користувача?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Користувач має налаштувати свій профіль після створення.\n\nБудь-який користувач пристрою може оновлювати додатки для решти користувачів."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Ви досягли ліміту користувачів"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувача.</item> @@ -847,7 +855,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Підключити новий пристрій"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Щоб транслювати цей сеанс, відкрийте додаток."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Невідомий додаток"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Припинити трансляцію"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як працює трансляція"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляція"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Люди поблизу, які мають сумісні пристрої з Bluetooth, можуть слухати медіаконтент, який ви транслюєте."</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index f5228a282f48..92bc68a54255 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ہاں، جاری رکھیں"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"نیا صارف شامل کریں؟"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"جب آپ ایک نیا صارف شامل کرتے ہیں تو اس شخص کو اپنی جگہ کو ترتیب دینے کی ضرورت ہوتی ہے۔\n\nکوئی بھی صارف دیگر سبھی صارفین کیلئے ایپس کو اپ ڈیٹ کر سکتا ہے۔"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"صارف کی حد مکمل ہو گئی"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">صرف <xliff:g id="COUNT">%d</xliff:g> صارفین بنائے جا سکتے ہیں۔</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"نئے آلہ کا جوڑا بنائیں"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"اس سیشن کو کاسٹ کرنے کیلئے، براہ کرم ایپ کھولیں۔"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"نامعلوم ایپ"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"کاسٹ کرنا بند کریں"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"براڈکاسٹنگ کیسے کام کرتا ہے"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"براڈکاسٹ"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"موافق بلوٹوتھ آلات کے ساتھ آپ کے قریبی لوگ آپ کے نشر کردہ میڈیا کو سن سکتے ہیں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index a2ce6bebb823..2a5ed8dbf15a 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -338,6 +338,11 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ha, davom ettirilsin"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mehmon rejimi"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Mehmon rejimidasiz"</string> + <string name="user_add_user_title" msgid="4172327541504825032">"Foydalanuvchi qo‘shilsinmi?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Yangi profil qo‘shilgach, uni sozlash lozim.\n\nQurilmaning istalgan foydalanuvchisi ilovalarni barcha hisoblar uchun yangilashi mumkin."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yangi foydalanuvchi kiritilsa, mehmon rejimi tark etiladi va joriy mehmon seansidagi barcha ilova va ularning maʼlumotlari tozalanadi."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limitga yetib keldi"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> tagacha foydalanuvchi qo‘shish mumkin.</item> @@ -834,7 +839,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yangi qurilmani ulash"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Bu seansni translatsiya qilish uchun ilovani oching."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Notanish ilova"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Toʻxtatish"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Translatsiya qanday ishlaydi"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Translatsiya"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Atrofingizdagi mos Bluetooth qurilmasiga ega foydalanuvchilar siz translatsiya qilayotgan mediani tinglay olishadi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 872f6cec3d33..551c6c25bcb0 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -341,6 +341,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Có, tiếp tục"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Thêm người dùng mới?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Khi bạn thêm người dùng mới, họ cần thiết lập không gian của mình.\n\nMọi người dùng đều có thể cập nhật ứng dụng cho tất cả người dùng khác."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Đã đạt đến giới hạn người dùng"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Bạn có thể thêm tối đa <xliff:g id="COUNT">%d</xliff:g> người dùng.</item> @@ -838,7 +846,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Ghép nối thiết bị mới"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Vui lòng mở ứng dụng để truyền phiên này."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ứng dụng không xác định"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dừng truyền"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cách tính năng truyền hoạt động"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Truyền"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Những người ở gần có thiết bị Bluetooth tương thích có thể nghe nội dung nghe nhìn bạn đang truyền"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index b77e008dc48e..c5cf230bdfef 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是,继续"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"要添加新用户吗?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"当您添加新用户时,该用户必须设置自己的空间。\n\n任何用户均可为其他所有用户更新应用。"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"已达到用户数上限"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">您最多可以添加 <xliff:g id="COUNT">%d</xliff:g> 位用户。</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"与新设备配对"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"如需投射此会话,请打开相关应用。"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"未知应用"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投射"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"广播的运作方式"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"广播"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近使用兼容蓝牙设备的用户可以收听您广播的媒体内容"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 9b1839305ccc..cbb2960d6290 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是的,請繼續"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"新增使用者?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"新增的使用者需要自行設定個人空間。\n\n任何使用者均可為所有其他使用者更新應用程式。"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"已達到使用者上限"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">您可以加入多達 <xliff:g id="COUNT">%d</xliff:g> 個使用者。</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"如要投放此工作階段,請開啟應用程式。"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明應用程式"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播運作方式"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近有兼容藍牙裝置的人可收聽您正在廣播的媒體內容"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 402679ac270c..05c41dd24217 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是,繼續"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"要新增使用者嗎?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"新增的使用者需要自行設定個人空間。\n\n任何使用者皆可為其他所有使用者更新應用程式。"</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"已達使用者數量上限"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">最多可新增 <xliff:g id="COUNT">%d</xliff:g> 位使用者。</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"如要投放這個工作階段,請開啟應用程式。"</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明的應用程式"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播功能的運作方式"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"如果附近的人有相容的藍牙裝置,就可以聽到你正在廣播的媒體內容"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index d85703bb61c2..f20b556a53f3 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -338,6 +338,14 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yebo, qhubeka"</string> + <!-- no translation found for guest_notification_app_name (2110425506754205509) --> + <skip /> + <!-- no translation found for guest_notification_session_active (5567273684713471450) --> + <skip /> + <string name="user_add_user_title" msgid="4172327541504825032">"Engeza umsebenzisi omusha?"</string> + <string name="user_add_user_message_short" msgid="2599370307878014791">"Uma ungeza umsebenzisi omusha, loyo muntu udinga ukusetha isikhala sakhe.\n\nNoma yimuphi umsebenzisi angabuyekeza izinhlelo zokusebenza kubo bonke abasebenzisi."</string> + <!-- no translation found for user_add_user_message_guest_remove (5589286604543355007) --> + <skip /> <string name="user_limit_reached_title" msgid="2429229448830346057">"Kufinyelelwe kumkhawulo womsebenzisi"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Ungangeza kufikela kubasebenzisi abangu-<xliff:g id="COUNT">%d</xliff:g>.</item> @@ -835,7 +843,6 @@ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bhangqa idivayisi entsha"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Ukuze usakaze le seshini, sicela uvule i-app."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"I-app engaziwa"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Misa ukusakaza"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Indlela ukusakaza okusebenza ngayo"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Sakaza"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Abantu abaseduze nawe abanamadivayisi e-Bluetooth ahambisanayo bangalalela imidiya oyisakazayo"</string> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 24118d256fcc..a06201c44d72 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -179,12 +179,10 @@ <!-- media output dialog--> <color name="media_dialog_background" android:lstar="98">@color/material_dynamic_neutral90</color> - <color name="media_dialog_item_main_content">@color/material_dynamic_primary20</color> + <color name="media_dialog_active_item_main_content">@color/material_dynamic_primary10</color> + <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_primary40</color> + <color name="media_dialog_item_status">@color/material_dynamic_primary10</color> <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color> - <color name="media_dialog_connected_item_background">@color/material_dynamic_primary90</color> - <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color> - <color name="media_dialog_button_background">@color/material_dynamic_primary40</color> - <color name="media_dialog_solid_button_text">@color/material_dynamic_neutral95</color> <!-- controls --> <color name="control_primary_text">#E6FFFFFF</color> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d9c8e077432f..2aa155188881 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -901,6 +901,23 @@ <!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] --> <string name="guest_wipe_session_dontwipe">Yes, continue</string> + <!-- App name of the notification when guest mode is entered [CHAR LIMIT=35] --> + <string name="guest_notification_app_name">Guest mode</string> + <!-- Title of the notification when guest mode is entered [CHAR LIMIT=35] --> + <string name="guest_notification_session_active">You are in guest mode</string> + + <!-- Title for add user confirmation dialog [CHAR LIMIT=30] --> + <string name="user_add_user_title" msgid="2108112641783146007">Add new user?</string> + + <!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] --> + <string name="user_add_user_message_short" msgid="1511354412249044381">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string> + + <!-- Additional message for add user confirmation dialog that is appended when current user is + guest and guest is ephemeral. This is to warn users that current guest session + would get removed after a new user is added and switched to [CHAR LIMIT=none] --> + <string name="user_add_user_message_guest_remove">\n\nAdding a new user will exit guest mode + and delete all apps and data from the current guest session.</string> + <!-- Title for the dialog that lets users know that the maximum allowed number of users on the device has been reached. [CHAR LIMIT=35]--> <string name="user_limit_reached_title">User limit reached</string> @@ -2278,8 +2295,6 @@ <string name="media_output_dialog_launch_app_text">To cast this session, please open the app.</string> <!-- App name when can't get app name [CHAR LIMIT=60] --> <string name="media_output_dialog_unknown_launch_app_name">Unknown app</string> - <!-- Button text for stopping casting [CHAR LIMIT=60] --> - <string name="media_output_dialog_button_stop_casting">Stop casting</string> <!-- Media Output Broadcast Dialog --> <!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index d7799a7addd1..2806ce8a115a 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -486,7 +486,7 @@ <style name="MediaOutputItemInactiveTitle"> <item name="android:textSize">16sp</item> - <item name="android:textColor">@color/media_dialog_item_main_content</item> + <item name="android:textColor">@color/media_dialog_inactive_item_main_content</item> </style> <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings"> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java index 4394ecbf79eb..98212e1d91b6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java @@ -25,8 +25,8 @@ import com.android.internal.util.TraceBuffer; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.LinkedList; import java.util.Queue; import java.util.function.Consumer; @@ -50,7 +50,7 @@ public class FrameProtoTracer<P, S extends P, T extends P, R> private final File mTraceFile; private final ProtoTraceParams<P, S, T, R> mParams; private Choreographer mChoreographer; - private final Queue<T> mPool = new LinkedList<>(); + private final Queue<T> mPool = new ArrayDeque<>(); private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>(); private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>(); diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt index 19d39d515325..d44598049806 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt @@ -284,6 +284,8 @@ class AnimatableClockView @JvmOverloads constructor( ) } + private val glyphFilter: GlyphCallback? = null // Add text animation tweak here. + /** * Set text style with an optional animation. * @@ -315,6 +317,7 @@ class AnimatableClockView @JvmOverloads constructor( delay = delay, onAnimationEnd = onAnimationEnd ) + textAnimator?.glyphFilter = glyphFilter } else { // when the text animator is set, update its start values onTextAnimatorInitialized = Runnable { @@ -328,6 +331,7 @@ class AnimatableClockView @JvmOverloads constructor( delay = delay, onAnimationEnd = onAnimationEnd ) + textAnimator?.glyphFilter = glyphFilter } } } diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt index 336101528450..ade89af81bd7 100644 --- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt +++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt @@ -22,12 +22,14 @@ import android.animation.TimeInterpolator import android.animation.ValueAnimator import android.graphics.Canvas import android.graphics.Typeface +import android.graphics.fonts.Font import android.text.Layout import android.util.SparseArray private const val TAG_WGHT = "wght" private const val DEFAULT_ANIMATION_DURATION: Long = 300 +typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit /** * This class provides text animation between two styles. * @@ -74,6 +76,59 @@ class TextAnimator( }) } + sealed class PositionedGlyph { + + /** + * Mutable X coordinate of the glyph position relative from drawing offset. + */ + var x: Float = 0f + + /** + * Mutable Y coordinate of the glyph position relative from the baseline. + */ + var y: Float = 0f + + /** + * Mutable text size of the glyph in pixels. + */ + var textSize: Float = 0f + + /** + * Mutable color of the glyph. + */ + var color: Int = 0 + + /** + * Immutable character offset in the text that the current font run start. + */ + abstract var runStart: Int + protected set + + /** + * Immutable run length of the font run. + */ + abstract var runLength: Int + protected set + + /** + * Immutable glyph index of the font run. + */ + abstract var glyphIndex: Int + protected set + + /** + * Immutable font instance for this font run. + */ + abstract var font: Font + protected set + + /** + * Immutable glyph ID for this glyph. + */ + abstract var glyphId: Int + protected set + } + private val typefaceCache = SparseArray<Typeface?>() fun updateLayout(layout: Layout) { @@ -84,6 +139,57 @@ class TextAnimator( return animator.isRunning } + /** + * GlyphFilter applied just before drawing to canvas for tweaking positions and text size. + * + * This callback is called for each glyphs just before drawing the glyphs. This function will + * be called with the intrinsic position, size, color, glyph ID and font instance. You can + * mutate the position, size and color for tweaking animations. + * Do not keep the reference of passed glyph object. The interpolator reuses that object for + * avoiding object allocations. + * + * Details: + * The text is drawn with font run units. The font run is a text segment that draws with the + * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text + * that current glyph is in. Once the font run is determined, the system will convert characters + * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and + * {@code glyphIndex} is the offset of the converted glyph array. Please note that the + * {@code glyphIndex} is not a character index, because the character will not be converted to + * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be + * composed from multiple characters. + * + * Here is an example of font runs: "fin. 終わり" + * + * Characters : f i n . _ 終 わ り + * Code Points: \u0066 \u0069 \u006E \u002E \u0020 \u7D42 \u308F \u308A + * Font Runs : <-- Roboto-Regular.ttf --><-- NotoSans-CJK.otf --> + * runStart = 0, runLength = 5 runStart = 5, runLength = 3 + * Glyph IDs : 194 48 7 8 4367 1039 1002 + * Glyph Index: 0 1 2 3 0 1 2 + * + * In this example, the "fi" is converted into ligature form, thus the single glyph ID is + * assigned for two characters, f and i. + * + * Example: + * ``` + * private val glyphFilter: GlyphCallback = { glyph, progress -> + * val index = glyph.runStart + * val i = glyph.glyphIndex + * val moveAmount = 1.3f + * val sign = (-1 + 2 * ((i + index) % 2)) + * val turnProgress = if (progress < .5f) progress / 0.5f else (1.0f - progress) / 0.5f + * + * // You can modify (x, y) coordinates, textSize and color during animation. + * glyph.textSize += glyph.textSize * sign * moveAmount * turnProgress + * glyph.y += glyph.y * sign * moveAmount * turnProgress + * glyph.x += glyph.x * sign * moveAmount * turnProgress + * } + * ``` + */ + var glyphFilter: GlyphCallback? + get() = textInterpolator.glyphFilter + set(value) { textInterpolator.glyphFilter = value } + fun draw(c: Canvas) = textInterpolator.draw(c) /** diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt index 5d5797cbbb3d..20dbe29efb35 100644 --- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt +++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt @@ -89,8 +89,11 @@ class TextInterpolator( private var lines = listOf<Line>() private val fontInterpolator = FontInterpolator() - // Recycling object for glyph drawing. Will be extended for the longest font run if needed. - private val tmpDrawPaint = TextPaint() + // Recycling object for glyph drawing and tweaking. + private val tmpPaint = TextPaint() + private val tmpPaintForGlyph by lazy { TextPaint() } + private val tmpGlyph by lazy { MutablePositionedGlyph() } + // Will be extended for the longest font run if needed. private var tmpPositionArray = FloatArray(20) /** @@ -206,8 +209,8 @@ class TextInterpolator( } else if (progress == 1f) { basePaint.set(targetPaint) } else { - lerp(basePaint, targetPaint, progress, tmpDrawPaint) - basePaint.set(tmpDrawPaint) + lerp(basePaint, targetPaint, progress, tmpPaint) + basePaint.set(tmpPaint) } lines.forEach { line -> @@ -231,7 +234,7 @@ class TextInterpolator( * @param canvas a canvas. */ fun draw(canvas: Canvas) { - lerp(basePaint, targetPaint, progress, tmpDrawPaint) + lerp(basePaint, targetPaint, progress, tmpPaint) lines.forEachIndexed { lineNo, line -> line.runs.forEach { run -> canvas.save() @@ -241,7 +244,7 @@ class TextInterpolator( canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat()) run.fontRuns.forEach { fontRun -> - drawFontRun(canvas, run, fontRun, tmpDrawPaint) + drawFontRun(canvas, run, fontRun, tmpPaint) } } finally { canvas.restore() @@ -330,24 +333,82 @@ class TextInterpolator( } } + private class MutablePositionedGlyph : TextAnimator.PositionedGlyph() { + override var runStart: Int = 0 + public set + override var runLength: Int = 0 + public set + override var glyphIndex: Int = 0 + public set + override lateinit var font: Font + public set + override var glyphId: Int = 0 + public set + } + + var glyphFilter: GlyphCallback? = null + // Draws single font run. private fun drawFontRun(c: Canvas, line: Run, run: FontRun, paint: Paint) { var arrayIndex = 0 + val font = fontInterpolator.lerp(run.baseFont, run.targetFont, progress) + + val glyphFilter = glyphFilter + if (glyphFilter == null) { + for (i in run.start until run.end) { + tmpPositionArray[arrayIndex++] = + MathUtils.lerp(line.baseX[i], line.targetX[i], progress) + tmpPositionArray[arrayIndex++] = + MathUtils.lerp(line.baseY[i], line.targetY[i], progress) + } + c.drawGlyphs(line.glyphIds, run.start, tmpPositionArray, 0, run.length, font, paint) + return + } + + tmpGlyph.font = font + tmpGlyph.runStart = run.start + tmpGlyph.runLength = run.end - run.start + + tmpPaintForGlyph.set(paint) + var prevStart = run.start + for (i in run.start until run.end) { - tmpPositionArray[arrayIndex++] = - MathUtils.lerp(line.baseX[i], line.targetX[i], progress) - tmpPositionArray[arrayIndex++] = - MathUtils.lerp(line.baseY[i], line.targetY[i], progress) + tmpGlyph.glyphId = line.glyphIds[i] + tmpGlyph.x = MathUtils.lerp(line.baseX[i], line.targetX[i], progress) + tmpGlyph.y = MathUtils.lerp(line.baseY[i], line.targetY[i], progress) + tmpGlyph.textSize = paint.textSize + tmpGlyph.color = paint.color + + glyphFilter(tmpGlyph, progress) + + if (tmpGlyph.textSize != paint.textSize || tmpGlyph.color != paint.color) { + tmpPaintForGlyph.textSize = tmpGlyph.textSize + tmpPaintForGlyph.color = tmpGlyph.color + + c.drawGlyphs( + line.glyphIds, + prevStart, + tmpPositionArray, + 0, + i - prevStart, + font, + tmpPaintForGlyph) + prevStart = i + arrayIndex = 0 + } + + tmpPositionArray[arrayIndex++] = tmpGlyph.x + tmpPositionArray[arrayIndex++] = tmpGlyph.y } c.drawGlyphs( line.glyphIds, - run.start, + prevStart, tmpPositionArray, 0, - run.length, - fontInterpolator.lerp(run.baseFont, run.targetFont, progress), - paint) + run.end - prevStart, + font, + tmpPaintForGlyph) } private fun updatePositionsAndFonts( diff --git a/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java new file mode 100644 index 000000000000..fd84543ee50b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.os.UserHandle; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.qs.QSUserSwitcherEvent; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.policy.UserSwitcherController; + +import javax.inject.Inject; + +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + +/** + * Manages handling of guest session persistent notification + * and actions to reset guest or exit guest session + */ +public final class GuestResetOrExitSessionReceiver extends BroadcastReceiver { + + private static final String TAG = GuestResetOrExitSessionReceiver.class.getSimpleName(); + + /** + * Broadcast sent to the system when guest user needs to be reset. + * This is only sent to registered receivers, not manifest receivers. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_GUEST_RESET = "android.intent.action.GUEST_RESET"; + + /** + * Broadcast sent to the system when guest user needs to exit. + * This is only sent to registered receivers, not manifest receivers. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_GUEST_EXIT = "android.intent.action.GUEST_EXIT"; + + public AlertDialog mExitSessionDialog; + public AlertDialog mResetSessionDialog; + private final UserTracker mUserTracker; + private final BroadcastDispatcher mBroadcastDispatcher; + private final ResetSessionDialog.Factory mResetSessionDialogFactory; + private final ExitSessionDialog.Factory mExitSessionDialogFactory; + + @Inject + public GuestResetOrExitSessionReceiver(UserTracker userTracker, + BroadcastDispatcher broadcastDispatcher, + ResetSessionDialog.Factory resetSessionDialogFactory, + ExitSessionDialog.Factory exitSessionDialogFactory) { + mUserTracker = userTracker; + mBroadcastDispatcher = broadcastDispatcher; + mResetSessionDialogFactory = resetSessionDialogFactory; + mExitSessionDialogFactory = exitSessionDialogFactory; + } + + /** + * Register this receiver with the {@link BroadcastDispatcher} + */ + public void register() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_GUEST_RESET); + intentFilter.addAction(ACTION_GUEST_EXIT); + mBroadcastDispatcher.registerReceiver(this, intentFilter, null /* handler */, + UserHandle.SYSTEM); + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + cancelResetDialog(); + cancelExitDialog(); + + UserInfo currentUser = mUserTracker.getUserInfo(); + if (!currentUser.isGuest()) { + return; + } + + if (ACTION_GUEST_RESET.equals(action)) { + mResetSessionDialog = mResetSessionDialogFactory.create(currentUser.id); + mResetSessionDialog.show(); + } else if (ACTION_GUEST_EXIT.equals(action)) { + mExitSessionDialog = mExitSessionDialogFactory.create(currentUser.id, + currentUser.isEphemeral()); + mExitSessionDialog.show(); + } + } + + private void cancelResetDialog() { + if (mResetSessionDialog != null && mResetSessionDialog.isShowing()) { + mResetSessionDialog.cancel(); + mResetSessionDialog = null; + } + } + + private void cancelExitDialog() { + if (mExitSessionDialog != null && mExitSessionDialog.isShowing()) { + mExitSessionDialog.cancel(); + mExitSessionDialog = null; + } + } + + /** + * Dialog shown when asking for confirmation before + * reset and restart of guest user. + */ + public static final class ResetSessionDialog extends SystemUIDialog implements + DialogInterface.OnClickListener { + + private final UserSwitcherController mUserSwitcherController; + private final UiEventLogger mUiEventLogger; + private final int mUserId; + + /** Factory class to create guest reset dialog instance */ + @AssistedFactory + public interface Factory { + /** Create a guest reset dialog instance */ + ResetSessionDialog create(int userId); + } + + @AssistedInject + ResetSessionDialog(Context context, + UserSwitcherController userSwitcherController, + UiEventLogger uiEventLogger, + @Assisted int userId) { + super(context); + + setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title); + setMessage(context.getString( + com.android.settingslib.R.string.guest_reset_and_restart_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_reset_guest_confirm_button), this); + setCanceledOnTouchOutside(false); + + mUserSwitcherController = userSwitcherController; + mUiEventLogger = uiEventLogger; + mUserId = userId; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); + mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + cancel(); + } + } + } + + /** + * Dialog shown when asking for confirmation before + * exit of guest user. + */ + public static final class ExitSessionDialog extends SystemUIDialog implements + DialogInterface.OnClickListener { + + private final UserSwitcherController mUserSwitcherController; + private final int mUserId; + private boolean mIsEphemeral; + + /** Factory class to create guest exit dialog instance */ + @AssistedFactory + public interface Factory { + /** Create a guest exit dialog instance */ + ExitSessionDialog create(int userId, boolean isEphemeral); + } + + @AssistedInject + ExitSessionDialog(Context context, + UserSwitcherController userSwitcherController, + @Assisted int userId, + @Assisted boolean isEphemeral) { + super(context); + + if (isEphemeral) { + setTitle(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_title)); + setMessage(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_dialog_button), this); + } else { + setTitle(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_title_non_ephemeral)); + setMessage(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_message_non_ephemeral)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_NEGATIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_clear_data_button), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_save_data_button), this); + } + setCanceledOnTouchOutside(false); + + mUserSwitcherController = userSwitcherController; + mUserId = userId; + mIsEphemeral = isEphemeral; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mIsEphemeral) { + if (which == DialogInterface.BUTTON_POSITIVE) { + // Ephemeral guest: exit guest, guest is removed by the system + // on exit, since its marked ephemeral + mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + // Cancel clicked, do nothing + cancel(); + } + } else { + if (which == DialogInterface.BUTTON_POSITIVE) { + // Non-ephemeral guest: exit guest, guest is not removed by the system + // on exit, since its marked non-ephemeral + mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false); + } else if (which == DialogInterface.BUTTON_NEGATIVE) { + // Non-ephemeral guest: remove guest and then exit + mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, true); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + // Cancel clicked, do nothing + cancel(); + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 9a6020f8556b..76a7cad15419 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -35,12 +35,18 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.settings.SecureSettings; +import javax.inject.Inject; + +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + /** * Manages notification when a guest session is resumed. */ public class GuestResumeSessionReceiver extends BroadcastReceiver { - private static final String TAG = "GuestResumeSessionReceiver"; + private static final String TAG = GuestResumeSessionReceiver.class.getSimpleName(); @VisibleForTesting public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in"; @@ -48,27 +54,31 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { @VisibleForTesting public AlertDialog mNewSessionDialog; private final UserTracker mUserTracker; - private final UserSwitcherController mUserSwitcherController; - private final UiEventLogger mUiEventLogger; private final SecureSettings mSecureSettings; - - public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController, - UserTracker userTracker, UiEventLogger uiEventLogger, - SecureSettings secureSettings) { - mUserSwitcherController = userSwitcherController; + private final BroadcastDispatcher mBroadcastDispatcher; + private final ResetSessionDialog.Factory mResetSessionDialogFactory; + private final GuestSessionNotification mGuestSessionNotification; + + @Inject + public GuestResumeSessionReceiver( + UserTracker userTracker, + SecureSettings secureSettings, + BroadcastDispatcher broadcastDispatcher, + GuestSessionNotification guestSessionNotification, + ResetSessionDialog.Factory resetSessionDialogFactory) { mUserTracker = userTracker; - mUiEventLogger = uiEventLogger; mSecureSettings = secureSettings; + mBroadcastDispatcher = broadcastDispatcher; + mGuestSessionNotification = guestSessionNotification; + mResetSessionDialogFactory = resetSessionDialogFactory; } /** * Register this receiver with the {@link BroadcastDispatcher} - * - * @param broadcastDispatcher to register the receiver. */ - public void register(BroadcastDispatcher broadcastDispatcher) { + public void register() { IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED); - broadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM); + mBroadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM); } @Override @@ -89,14 +99,25 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { return; } - int notFirstLogin = mSecureSettings.getIntForUser( + int guestLoginState = mSecureSettings.getIntForUser( SETTING_GUEST_HAS_LOGGED_IN, 0, userId); - if (notFirstLogin != 0) { - mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController, - mUiEventLogger, userId); + + if (guestLoginState == 0) { + // set 1 to indicate, 1st login + guestLoginState = 1; + mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId); + } else if (guestLoginState == 1) { + // set 2 to indicate, 2nd or later login + guestLoginState = 2; + mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId); + } + + mGuestSessionNotification.createPersistentNotification(currentUser, + (guestLoginState <= 1)); + + if (guestLoginState > 1) { + mNewSessionDialog = mResetSessionDialogFactory.create(userId); mNewSessionDialog.show(); - } else { - mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId); } } } @@ -124,10 +145,19 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { private final UiEventLogger mUiEventLogger; private final int mUserId; - ResetSessionDialog(Context context, + + /** Factory class to create guest reset dialog instance */ + @AssistedFactory + public interface Factory { + /** Create a guest reset dialog instance */ + ResetSessionDialog create(int userId); + } + + @AssistedInject + public ResetSessionDialog(Context context, UserSwitcherController userSwitcherController, UiEventLogger uiEventLogger, - int userId) { + @Assisted int userId) { super(context, false /* dismissOnDeviceLock */); setTitle(context.getString(R.string.guest_wipe_session_title)); diff --git a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java new file mode 100644 index 000000000000..b0eaab97c5ac --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.UserInfo; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.FeatureFlagUtils; + +import com.android.internal.messages.nano.SystemMessageProto; +import com.android.systemui.util.NotificationChannels; + +import javax.inject.Inject; + +/** + * Posts a persistent notification on entry to guest mode + */ +public final class GuestSessionNotification { + + private static final String TAG = GuestSessionNotification.class.getSimpleName(); + + private final Context mContext; + private final NotificationManager mNotificationManager; + + @Inject + public GuestSessionNotification(Context context, + NotificationManager notificationManager) { + mContext = context; + mNotificationManager = notificationManager; + } + + private void overrideNotificationAppName(Notification.Builder notificationBuilder) { + final Bundle extras = new Bundle(); + String appName = mContext.getString(R.string.guest_notification_app_name); + + extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName); + + notificationBuilder.addExtras(extras); + } + + void createPersistentNotification(UserInfo userInfo, boolean isGuestFirstLogin) { + if (!FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES) + || !userInfo.isGuest()) { + // we create a persistent notification only if enabled and only for guests + return; + } + String contentText; + if (userInfo.isEphemeral()) { + contentText = mContext.getString(R.string.guest_notification_ephemeral); + } else if (isGuestFirstLogin) { + contentText = mContext.getString(R.string.guest_notification_non_ephemeral); + } else { + contentText = mContext.getString( + R.string.guest_notification_non_ephemeral_non_first_login); + } + + final Intent guestExitIntent = new Intent( + GuestResetOrExitSessionReceiver.ACTION_GUEST_EXIT); + final Intent userSettingsIntent = new Intent(Settings.ACTION_USER_SETTINGS); + + PendingIntent guestExitPendingIntent = + PendingIntent.getBroadcastAsUser(mContext, 0, guestExitIntent, + PendingIntent.FLAG_IMMUTABLE, + UserHandle.SYSTEM); + + PendingIntent userSettingsPendingIntent = + PendingIntent.getActivityAsUser(mContext, 0, userSettingsIntent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + null, + UserHandle.of(userInfo.id)); + + Notification.Builder builder = new Notification.Builder(mContext, + NotificationChannels.ALERTS) + .setSmallIcon(R.drawable.ic_account_circle) + .setContentTitle(mContext.getString(R.string.guest_notification_session_active)) + .setContentText(contentText) + .setPriority(Notification.PRIORITY_DEFAULT) + .setOngoing(true) + .setContentIntent(userSettingsPendingIntent); + + // we show reset button only if this is a 2nd or later login + if (!isGuestFirstLogin) { + final Intent guestResetIntent = new Intent( + GuestResetOrExitSessionReceiver.ACTION_GUEST_RESET); + + PendingIntent guestResetPendingIntent = + PendingIntent.getBroadcastAsUser(mContext, 0, guestResetIntent, + PendingIntent.FLAG_IMMUTABLE, + UserHandle.SYSTEM); + + builder.addAction(R.drawable.ic_sysbar_home, + mContext.getString( + com.android.settingslib.R.string.guest_reset_guest_confirm_button), + guestResetPendingIntent); + } + builder.addAction(R.drawable.ic_sysbar_home, + mContext.getString( + com.android.settingslib.R.string.guest_exit_button), + guestExitPendingIntent); + + overrideNotificationAppName(builder); + + mNotificationManager.notifyAsUser(null, + SystemMessageProto.SystemMessage.NOTE_GUEST_SESSION, + builder.build(), + UserHandle.of(userInfo.id)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java index e5da38936593..4773f2a3b13e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java @@ -18,9 +18,9 @@ package com.android.systemui.classifier; import android.view.MotionEvent; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -33,13 +33,13 @@ import java.util.ListIterator; */ public class TimeLimitedMotionEventBuffer implements List<MotionEvent> { - private final LinkedList<MotionEvent> mMotionEvents; + private final List<MotionEvent> mMotionEvents; private final long mMaxAgeMs; public TimeLimitedMotionEventBuffer(long maxAgeMs) { super(); mMaxAgeMs = maxAgeMs; - mMotionEvents = new LinkedList<>(); + mMotionEvents = new ArrayList<>(); } private void ejectOldEvents() { @@ -47,7 +47,7 @@ public class TimeLimitedMotionEventBuffer implements List<MotionEvent> { return; } Iterator<MotionEvent> iter = listIterator(); - long mostRecentMs = mMotionEvents.getLast().getEventTime(); + long mostRecentMs = mMotionEvents.get(mMotionEvents.size() - 1).getEventTime(); while (iter.hasNext()) { MotionEvent ev = iter.next(); if (mostRecentMs - ev.getEventTime() > mMaxAgeMs) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index bed553e6e4d6..2ea596788091 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -33,6 +33,7 @@ import android.service.controls.actions.ControlAction import android.util.ArrayMap import android.util.Log import com.android.internal.annotations.VisibleForTesting +import com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID import com.android.systemui.Dumpable import com.android.systemui.backup.BackupHelper import com.android.systemui.broadcast.BroadcastDispatcher @@ -43,6 +44,7 @@ import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index f9115b20ca06..3eb58bba1ca4 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -343,7 +343,7 @@ private class ControlHolderAccessibilityDelegate( info.className = Switch::class.java.name } - override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean { + override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean { if (super.performAccessibilityAction(host, action, args)) { return true } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java index 0cf3333d12a6..8ba6f1c4a411 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java @@ -18,6 +18,8 @@ package com.android.systemui.dagger; import android.content.BroadcastReceiver; +import com.android.systemui.GuestResetOrExitSessionReceiver; +import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.media.dialog.MediaOutputDialogReceiver; import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; @@ -89,4 +91,21 @@ public abstract class DefaultBroadcastReceiverBinder { public abstract BroadcastReceiver bindPeopleSpaceWidgetProvider( PeopleSpaceWidgetProvider broadcastReceiver); + /** + * + */ + @Binds + @IntoMap + @ClassKey(GuestResumeSessionReceiver.class) + public abstract BroadcastReceiver bindGuestResumeSessionReceiver( + GuestResumeSessionReceiver broadcastReceiver); + + /** + * + */ + @Binds + @IntoMap + @ClassKey(GuestResetOrExitSessionReceiver.class) + public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver( + GuestResetOrExitSessionReceiver broadcastReceiver); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 5d154c3b4f6b..c870b89d9e0a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -178,7 +178,8 @@ public abstract class SystemUIDefaultModule { KeyguardBypassController bypassController, GroupMembershipManager groupManager, VisualStabilityProvider visualStabilityProvider, - ConfigurationController configurationController) { + ConfigurationController configurationController, + @Main Handler handler) { return new HeadsUpManagerPhone( context, headsUpManagerLogger, @@ -186,7 +187,8 @@ public abstract class SystemUIDefaultModule { bypassController, groupManager, visualStabilityProvider, - configurationController + configurationController, + handler ); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java index aaa4d9eefd29..c4531b573d22 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java @@ -42,10 +42,14 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; +import com.android.systemui.statusbar.commandline.Command; +import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; +import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TreeMap; @@ -59,6 +63,10 @@ import javax.inject.Named; * * Flags can be set (or unset) via the following adb command: * + * adb shell cmd statusbar flag <id> <on|off|toggle|erase> + * + * Alternatively, you can change flags via a broadcast intent: + * * adb shell am broadcast -a com.android.systemui.action.SET_FLAG --ei id <id> [--ez value <0|1>] * * To restore a flag back to its default, leave the `--ez value <0|1>` off of the command. @@ -67,6 +75,7 @@ import javax.inject.Named; public class FeatureFlagsDebug implements FeatureFlags, Dumpable { private static final String TAG = "SysUIFlags"; static final String ALL_FLAGS = "all_flags"; + private static final String FLAG_COMMAND = "flag"; private final FlagManager mFlagManager; private final SecureSettings mSecureSettings; @@ -86,12 +95,15 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { @Main Resources resources, DumpManager dumpManager, @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags, + CommandRegistry commandRegistry, IStatusBarService barService) { mFlagManager = flagManager; mSecureSettings = secureSettings; mResources = resources; mSystemProperties = systemProperties; mAllFlags = allFlags; + mBarService = barService; + IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_SET_FLAG); filter.addAction(ACTION_GET_FLAGS); @@ -100,7 +112,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { context.registerReceiver(mReceiver, filter, null, null, Context.RECEIVER_EXPORTED_UNAUDITED); dumpManager.registerDumpable(TAG, this); - mBarService = barService; + commandRegistry.registerCommand(FLAG_COMMAND, FlagCommand::new); } @Override @@ -276,6 +288,31 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { } } + private void setBooleanFlagInternal(Flag<?> flag, boolean value) { + if (flag instanceof BooleanFlag) { + setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE); + } else if (flag instanceof ResourceBooleanFlag) { + setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE); + } else if (flag instanceof SysPropBooleanFlag) { + // Store SysProp flags in SystemProperties where they can read by outside parties. + mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value); + dispatchListenersAndMaybeRestart(flag.getId(), + FeatureFlagsDebug.this::restartAndroid); + } else { + throw new IllegalArgumentException("Unknown flag type"); + } + } + + private void setStringFlagInternal(Flag<?> flag, String value) { + if (flag instanceof StringFlag) { + setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE); + } else if (flag instanceof ResourceStringFlag) { + setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE); + } else { + throw new IllegalArgumentException("Unknown flag type"); + } + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -327,24 +364,19 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { } Object value = extras.get(EXTRA_VALUE); - if (flag instanceof BooleanFlag && value instanceof Boolean) { - setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE); - } else if (flag instanceof ResourceBooleanFlag && value instanceof Boolean) { - setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE); - } else if (flag instanceof SysPropBooleanFlag && value instanceof Boolean) { - // Store SysProp flags in SystemProperties where they can read by outside parties. - mSystemProperties.setBoolean( - ((SysPropBooleanFlag) flag).getName(), (Boolean) value); - dispatchListenersAndMaybeRestart(flag.getId(), - FeatureFlagsDebug.this::restartAndroid); - } else if (flag instanceof StringFlag && value instanceof String) { - setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE); - } else if (flag instanceof ResourceStringFlag && value instanceof String) { - setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE); - } else { + + try { + if (value instanceof Boolean) { + setBooleanFlagInternal(flag, (Boolean) value); + } else if (value instanceof String) { + setStringFlagInternal(flag, (String) value); + } else { + throw new IllegalArgumentException("Unknown value type"); + } + } catch (IllegalArgumentException e) { Log.w(TAG, - "Unable to set " + id + " of type " + flag.getClass() + " to value of type " - + (value == null ? null : value.getClass())); + "Unable to set " + flag.getId() + " of type " + flag.getClass() + + " to value of type " + (value == null ? null : value.getClass())); } } @@ -388,4 +420,153 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { mStringFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": [length=" + value.length() + "] \"" + value + "\"")); } + + class FlagCommand implements Command { + private final List<String> mOnCommands = List.of("true", "on", "1", "enabled"); + private final List<String> mOffCommands = List.of("false", "off", "0", "disable"); + + @Override + public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) { + if (args.size() == 0) { + pw.println("Error: no flag id supplied"); + help(pw); + pw.println(); + printKnownFlags(pw); + return; + } + + if (args.size() > 2) { + pw.println("Invalid number of arguments."); + help(pw); + return; + } + + int id = 0; + try { + id = Integer.parseInt(args.get(0)); + if (!mAllFlags.containsKey(id)) { + pw.println("Unknown flag id: " + id); + pw.println(); + printKnownFlags(pw); + return; + } + } catch (NumberFormatException e) { + id = flagNameToId(args.get(0)); + if (id == 0) { + pw.println("Invalid flag. Must an integer id or flag name: " + args.get(0)); + return; + } + } + Flag<?> flag = mAllFlags.get(id); + + String cmd = ""; + if (args.size() == 2) { + cmd = args.get(1).toLowerCase(); + } + + if ("erase".equals(cmd) || "reset".equals(cmd)) { + eraseFlag(flag); + return; + } + + boolean newValue = true; + if (args.size() == 1 || "toggle".equals(cmd)) { + boolean enabled = isBooleanFlagEnabled(flag); + + if (args.size() == 1) { + pw.println("Flag " + id + " is " + enabled); + return; + } + + newValue = !enabled; + } else { + newValue = mOnCommands.contains(cmd); + if (!newValue && !mOffCommands.contains(cmd)) { + pw.println("Invalid on/off argument supplied"); + help(pw); + return; + } + } + + pw.flush(); // Next command will restart sysui, so flush before we do so. + setBooleanFlagInternal(flag, newValue); + } + + @Override + public void help(PrintWriter pw) { + pw.println( + "Usage: adb shell cmd statusbar flag <id> " + + "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]"); + pw.println("The id can either be a numeric integer or the corresponding field name"); + pw.println( + "If no argument is supplied after the id, the flags runtime value is output"); + } + + private boolean isBooleanFlagEnabled(Flag<?> flag) { + if (flag instanceof BooleanFlag) { + return isEnabled((BooleanFlag) flag); + } else if (flag instanceof ResourceBooleanFlag) { + return isEnabled((ResourceBooleanFlag) flag); + } else if (flag instanceof SysPropFlag) { + return isEnabled((SysPropBooleanFlag) flag); + } + + return false; + } + + private int flagNameToId(String flagName) { + List<Field> fields = Flags.getFlagFields(); + for (Field field : fields) { + if (flagName.equals(field.getName())) { + return fieldToId(field); + } + } + + return 0; + } + + private int fieldToId(Field field) { + try { + Flag<?> flag = (Flag<?>) field.get(null); + return flag.getId(); + } catch (IllegalAccessException e) { + // no-op + } + + return 0; + } + + private void printKnownFlags(PrintWriter pw) { + List<Field> fields = Flags.getFlagFields(); + + int longestFieldName = 0; + for (Field field : fields) { + longestFieldName = Math.max(longestFieldName, field.getName().length()); + } + + pw.println("Known Flags:"); + pw.print("Flag Name"); + for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) { + pw.print(" "); + } + pw.println("ID Enabled?"); + for (int i = 0; i < longestFieldName; i++) { + pw.print("="); + } + pw.println(" ==== ========"); + for (Field field : fields) { + int id = fieldToId(field); + if (id == 0 || !mAllFlags.containsKey(id)) { + continue; + } + pw.print(field.getName()); + int fieldWidth = field.getName().length(); + for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) { + pw.print(" "); + } + pw.printf("%-4d ", id); + pw.println(isBooleanFlagEnabled(mAllFlags.get(id))); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index d71782d70621..94418f498d3b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -20,7 +20,9 @@ import com.android.internal.annotations.Keep; import com.android.systemui.R; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -184,25 +186,36 @@ public class Flags { if (sFlagMap != null) { return sFlagMap; } + Map<Integer, Flag<?>> flags = new HashMap<>(); + List<Field> flagFields = getFlagFields(); + + for (Field field : flagFields) { + try { + Flag<?> flag = (Flag<?>) field.get(null); + flags.put(flag.getId(), flag); + } catch (IllegalAccessException e) { + // no-op + } + } + sFlagMap = flags; + + return sFlagMap; + } + + static List<Field> getFlagFields() { Field[] fields = Flags.class.getFields(); + List<Field> result = new ArrayList<>(); for (Field field : fields) { Class<?> t = field.getType(); if (Flag.class.isAssignableFrom(t)) { - try { - Flag<?> flag = (Flag<?>) field.get(null); - flags.put(flag.getId(), flag); - } catch (IllegalAccessException e) { - // no-op - } + result.add(field); } } - sFlagMap = flags; - - return sFlagMap; + return result; } // | . . . . . . . . . . . . . . . . . . . | // | | diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java index 5aedbdc20b31..bd00ce61a7e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -34,8 +34,8 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -71,7 +71,7 @@ public class KeyguardIndicationRotateTextViewController extends @Nullable private ShowNextIndication mShowNextIndicationRunnable; // List of indication types to show. The next indication to show is always at index 0 - private final List<Integer> mIndicationQueue = new LinkedList<>(); + private final List<Integer> mIndicationQueue = new ArrayList<>(); private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE; private CharSequence mCurrMessage; private long mLastIndicationSwitch; diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 7f25642be5ee..5e810f24eada 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -33,6 +33,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE import com.android.systemui.tuner.TunerService import com.android.systemui.util.Utils import com.android.systemui.util.time.SystemClock diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 0edadcc82a24..9e4ee61669a1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -118,9 +118,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mCheckBox.setVisibility(View.GONE); mStatusIcon.setVisibility(View.GONE); mContainerLayout.setOnClickListener(null); - mTitleText.setTextColor(mController.getColorItemContent()); - mSubTitleText.setTextColor(mController.getColorItemContent()); - mTwoLineTitleText.setTextColor(mController.getColorItemContent()); + mTitleText.setTextColor(mController.getColorInactiveItem()); mSeekBar.getProgressDrawable().setColorFilter( new PorterDuffColorFilter(mController.getColorSeekbarProgress(), PorterDuff.Mode.SRC_IN)); @@ -141,7 +139,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { && !mController.hasAdjustVolumeUserRestriction()) { mProgressBar.getIndeterminateDrawable().setColorFilter( new PorterDuffColorFilter( - mController.getColorItemContent(), + mController.getColorInactiveItem(), PorterDuff.Mode.SRC_IN)); setSingleLineLayout(getItemTitle(device), true /* bFocused */, false /* showSeekBar*/, @@ -156,7 +154,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mTitleIcon.setAlpha(DEVICE_CONNECTED_ALPHA); mStatusIcon.setImageDrawable( mContext.getDrawable(R.drawable.media_output_status_failed)); - mStatusIcon.setColorFilter(mController.getColorItemContent()); + mStatusIcon.setColorFilter(mController.getColorInactiveItem()); setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */, false /* showProgressBar */, true /* showSubtitle */, true /* showStatus */); @@ -164,34 +162,40 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mContainerLayout.setOnClickListener(v -> onItemClick(v, device)); } else if (mController.getSelectedMediaDevice().size() > 1 && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) { - mTitleText.setTextColor(mController.getColorItemContent()); + mTitleText.setTextColor(mController.getColorActiveItem()); setSingleLineLayout(getItemTitle(device), true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */, false /* showStatus */); + mCheckBox.setOnCheckedChangeListener(null); mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(true); - mSeekBar.setOnClickListener(null); - mSeekBar.setOnClickListener(v -> onGroupActionTriggered(false, device)); - setCheckBoxColor(mCheckBox, mController.getColorItemContent()); + mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> { + onCheckBoxClicked(false, device); + }); + setCheckBoxColor(mCheckBox, mController.getColorActiveItem()); initSeekbar(device); } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) { mStatusIcon.setImageDrawable( mContext.getDrawable(R.drawable.media_output_status_check)); - mStatusIcon.setColorFilter(mController.getColorItemContent()); - mTitleText.setTextColor(mController.getColorItemContent()); + mStatusIcon.setColorFilter(mController.getColorActiveItem()); + mTitleText.setTextColor(mController.getColorActiveItem()); setSingleLineLayout(getItemTitle(device), true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */, true /* showStatus */); initSeekbar(device); mCurrentActivePosition = position; } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) { + mCheckBox.setOnCheckedChangeListener(null); mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(false); - mContainerLayout.setOnClickListener(v -> onGroupActionTriggered(true, device)); - setCheckBoxColor(mCheckBox, mController.getColorItemContent()); + mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> { + onCheckBoxClicked(true, device); + }); + setCheckBoxColor(mCheckBox, mController.getColorInactiveItem()); setSingleLineLayout(getItemTitle(device), false /* bFocused */, false /* showSeekBar */, false /* showProgressBar */, false /* showStatus */); + mContainerLayout.setOnClickListener(v -> onItemClick(v, device)); } else { setSingleLineLayout(getItemTitle(device), false /* bFocused */); mContainerLayout.setOnClickListener(v -> onItemClick(v, device)); @@ -209,7 +213,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) { if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) { - mTitleText.setTextColor(mController.getColorItemContent()); + mTitleText.setTextColor(mController.getColorInactiveItem()); mCheckBox.setVisibility(View.GONE); setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new), false /* bFocused */); @@ -221,7 +225,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } } - private void onGroupActionTriggered(boolean isChecked, MediaDevice device) { + private void onCheckBoxClicked(boolean isChecked, MediaDevice device) { if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) { mController.addDeviceToPlayMedia(device); } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(), diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index 5c536d469212..958e9ed9e38b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -183,16 +183,14 @@ public abstract class MediaOutputBaseAdapter extends void setSingleLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar, boolean showProgressBar, boolean showStatus) { mTwoLineLayout.setVisibility(View.GONE); - boolean isActive = showSeekBar || showProgressBar; final Drawable backgroundDrawable = - isActive + showSeekBar || showProgressBar ? mContext.getDrawable(R.drawable.media_output_item_background_active) .mutate() : mContext.getDrawable( R.drawable.media_output_item_background) .mutate(); backgroundDrawable.setColorFilter(new PorterDuffColorFilter( - isActive ? mController.getColorConnectedItemBackground() - : mController.getColorItemBackground(), + mController.getColorItemBackground(), PorterDuff.Mode.SRC_IN)); mItemLayout.setBackground(backgroundDrawable); mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE); @@ -381,7 +379,7 @@ public abstract class MediaOutputBaseAdapter extends .mutate(); drawable.setColorFilter( new PorterDuffColorFilter(Utils.getColorStateListDefaultColor(mContext, - R.color.media_dialog_item_main_content), + R.color.media_dialog_active_item_main_content), PorterDuff.Mode.SRC_IN)); return drawable; } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index bde772d95674..79be916b3137 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -218,9 +218,10 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements ColorFilter buttonColorFilter = new PorterDuffColorFilter( mAdapter.getController().getColorButtonBackground(), PorterDuff.Mode.SRC_IN); + ColorFilter onlineButtonColorFilter = new PorterDuffColorFilter( + mAdapter.getController().getColorInactiveItem(), PorterDuff.Mode.SRC_IN); mDoneButton.getBackground().setColorFilter(buttonColorFilter); - mStopButton.getBackground().setColorFilter(buttonColorFilter); - mDoneButton.setTextColor(mAdapter.getController().getColorPositiveButtonText()); + mStopButton.getBackground().setColorFilter(onlineButtonColorFilter); } mHeaderIcon.setVisibility(View.VISIBLE); mHeaderIcon.setImageIcon(icon); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index ec2a950051b7..af52a2fdc779 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -117,12 +117,11 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, private MediaOutputMetricLogger mMetricLogger; - private int mColorItemContent; + private int mColorActiveItem; + private int mColorInactiveItem; private int mColorSeekbarProgress; private int mColorButtonBackground; private int mColorItemBackground; - private int mColorConnectedItemBackground; - private int mColorPositiveButtonText; public enum BroadcastNotifyDialog { ACTION_FIRST_LAUNCH, @@ -147,18 +146,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName); mDialogLaunchAnimator = dialogLaunchAnimator; mNearbyMediaDevicesManager = nearbyMediaDevicesManagerOptional.orElse(null); - mColorItemContent = Utils.getColorStateListDefaultColor(mContext, - R.color.media_dialog_item_main_content); + mColorActiveItem = Utils.getColorStateListDefaultColor(mContext, + R.color.media_dialog_active_item_main_content); + mColorInactiveItem = Utils.getColorStateListDefaultColor(mContext, + R.color.media_dialog_inactive_item_main_content); mColorSeekbarProgress = Utils.getColorStateListDefaultColor(mContext, - R.color.media_dialog_seekbar_progress); + android.R.color.system_accent1_200); mColorButtonBackground = Utils.getColorStateListDefaultColor(mContext, - R.color.media_dialog_button_background); - mColorItemBackground = Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_item_background); - mColorConnectedItemBackground = Utils.getColorStateListDefaultColor(mContext, - R.color.media_dialog_connected_item_background); - mColorPositiveButtonText = Utils.getColorStateListDefaultColor(mContext, - R.color.media_dialog_solid_button_text); + mColorItemBackground = Utils.getColorStateListDefaultColor(mContext, + android.R.color.system_accent2_50); } void start(@NonNull Callback cb) { @@ -338,7 +335,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, } void setColorFilter(Drawable drawable, boolean isActive) { - drawable.setColorFilter(new PorterDuffColorFilter(mColorItemContent, + drawable.setColorFilter(new PorterDuffColorFilter(isActive + ? mColorActiveItem : mColorInactiveItem, PorterDuff.Mode.SRC_IN)); } @@ -373,32 +371,26 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, ColorScheme mCurrentColorScheme = new ColorScheme(wallpaperColors, isDarkTheme); if (isDarkTheme) { - mColorItemContent = mCurrentColorScheme.getAccent1().get(2); // A1-100 - mColorSeekbarProgress = mCurrentColorScheme.getAccent2().get(7); // A2-600 - mColorButtonBackground = mCurrentColorScheme.getAccent1().get(4); // A1-300 - mColorItemBackground = mCurrentColorScheme.getNeutral2().get(9); // N2-800 - mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().get(9); // A2-800 - mColorPositiveButtonText = mCurrentColorScheme.getAccent2().get(9); // A2-800 + mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10); + mColorInactiveItem = mCurrentColorScheme.getNeutral1().get(10); + mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(2); + mColorButtonBackground = mCurrentColorScheme.getAccent1().get(2); + mColorItemBackground = mCurrentColorScheme.getAccent2().get(0); } else { - mColorItemContent = mCurrentColorScheme.getAccent1().get(9); // A1-800 - mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(4); // A1-300 - mColorButtonBackground = mCurrentColorScheme.getAccent1().get(7); // A1-600 - mColorItemBackground = mCurrentColorScheme.getAccent2().get(1); // A2-50 - mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().get(2); // A1-100 - mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().get(1); // N1-50 + mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10); + mColorInactiveItem = mCurrentColorScheme.getAccent1().get(7); + mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(3); + mColorButtonBackground = mCurrentColorScheme.getAccent1().get(3); + mColorItemBackground = mCurrentColorScheme.getAccent2().get(0); } } - public int getColorConnectedItemBackground() { - return mColorConnectedItemBackground; - } - - public int getColorPositiveButtonText() { - return mColorPositiveButtonText; + public int getColorActiveItem() { + return mColorActiveItem; } - public int getColorItemContent() { - return mColorItemContent; + public int getColorInactiveItem() { + return mColorInactiveItem; } public int getColorSeekbarProgress() { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java deleted file mode 100644 index 72f308e4f6b1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.dialog; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.SeekBar; - -/** - * Customized seekbar used by MediaOutputDialog, which only changes progress when dragging, - * otherwise performs click. - */ -public class MediaOutputSeekbar extends SeekBar { - private int mLastDownPosition = -1; - - public MediaOutputSeekbar(Context context) { - super(context); - } - - public MediaOutputSeekbar(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mLastDownPosition = Math.round(event.getX()); - } else if (event.getAction() == MotionEvent.ACTION_UP) { - if (mLastDownPosition == event.getX()) { - performClick(); - return true; - } - mLastDownPosition = -1; - } - return super.onTouchEvent(event); - } - - @Override - public boolean performClick() { - return super.performClick(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java index 5510eb172cd7..09c4cb760c90 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java +++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java @@ -57,7 +57,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import javax.inject.Inject; @@ -126,7 +125,7 @@ public class TvOngoingPrivacyChip extends CoreStartable implements PrivacyItemCo private final Runnable mCollapseRunnable = this::collapseChip; private final Runnable mAccessibilityRunnable = this::makeAccessibilityAnnouncement; - private final List<PrivacyItem> mItemsBeforeLastAnnouncement = new LinkedList<>(); + private final List<PrivacyItem> mItemsBeforeLastAnnouncement = new ArrayList<>(); @State private int mState = STATE_NOT_SHOWN; diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index 3a6248b793d0..6b79a282c75f 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -30,9 +30,9 @@ import androidx.annotation.GuardedBy import androidx.annotation.WorkerThread import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager +import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE import com.android.systemui.util.Assert import java.io.PrintWriter -import java.lang.IllegalStateException import java.lang.ref.WeakReference import java.util.concurrent.Executor import kotlin.properties.ReadWriteProperty diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index 6cfbb43fa25a..1ffa6f41d8a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -19,13 +19,12 @@ package com.android.systemui.statusbar; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; -import android.os.Looper; import android.os.SystemClock; import android.util.ArrayMap; import android.util.ArraySet; import android.view.accessibility.AccessibilityEvent; -import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; @@ -43,8 +42,9 @@ public abstract class AlertingNotificationManager implements NotificationLifetim protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>(); protected final HeadsUpManagerLogger mLogger; - public AlertingNotificationManager(HeadsUpManagerLogger logger) { + public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler) { mLogger = logger; + mHandler = handler; } /** @@ -57,8 +57,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback; protected int mMinimumDisplayTime; protected int mAutoDismissNotificationDecay; - @VisibleForTesting - public Handler mHandler = new Handler(Looper.getMainLooper()); + private final Handler mHandler; /** * Called when posting a new notification that should alert the user and appear on screen. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index aac5b8d6b2eb..4895fa3fcb56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -19,12 +19,12 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index 57fd1975e13a..5ac481341d43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.SectionClassifier; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -28,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider; import com.android.systemui.statusbar.notification.collection.render.NodeController; import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; import com.android.systemui.statusbar.notification.dagger.AlertingHeader; @@ -51,7 +51,7 @@ public class RankingCoordinator implements Coordinator { public static final boolean SHOW_ALL_SECTIONS = false; private final StatusBarStateController mStatusBarStateController; private final HighPriorityProvider mHighPriorityProvider; - private final SectionClassifier mSectionClassifier; + private final SectionStyleProvider mSectionStyleProvider; private final NodeController mSilentNodeController; private final SectionHeaderController mSilentHeaderController; private final NodeController mAlertingHeaderController; @@ -62,13 +62,13 @@ public class RankingCoordinator implements Coordinator { public RankingCoordinator( StatusBarStateController statusBarStateController, HighPriorityProvider highPriorityProvider, - SectionClassifier sectionClassifier, + SectionStyleProvider sectionStyleProvider, @AlertingHeader NodeController alertingHeaderController, @SilentHeader SectionHeaderController silentHeaderController, @SilentHeader NodeController silentNodeController) { mStatusBarStateController = statusBarStateController; mHighPriorityProvider = highPriorityProvider; - mSectionClassifier = sectionClassifier; + mSectionStyleProvider = sectionStyleProvider; mAlertingHeaderController = alertingHeaderController; mSilentNodeController = silentNodeController; mSilentHeaderController = silentHeaderController; @@ -77,7 +77,7 @@ public class RankingCoordinator implements Coordinator { @Override public void attach(NotifPipeline pipeline) { mStatusBarStateController.addCallback(mStatusBarStateCallback); - mSectionClassifier.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner)); + mSectionStyleProvider.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner)); pipeline.addPreGroupFilter(mSuspendedFilter); pipeline.addPreGroupFilter(mDndVisualEffectsFilter); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt index 4e9d3ac07a96..9e8b35af1bce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt @@ -19,11 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.content.Context import com.android.systemui.R import com.android.systemui.statusbar.notification.AssistantFeedbackController -import com.android.systemui.statusbar.notification.SectionClassifier import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.collection.render.NotifRowController import javax.inject.Inject @@ -35,7 +35,7 @@ import javax.inject.Inject class RowAppearanceCoordinator @Inject internal constructor( context: Context, private var mAssistantFeedbackController: AssistantFeedbackController, - private var mSectionClassifier: SectionClassifier + private var mSectionStyleProvider: SectionStyleProvider ) : Coordinator { private var entryToExpand: NotificationEntry? = null @@ -55,7 +55,7 @@ class RowAppearanceCoordinator @Inject internal constructor( private fun onBeforeRenderList(list: List<ListEntry>) { entryToExpand = list.firstOrNull()?.representativeEntry?.takeIf { entry -> - !mSectionClassifier.isMinimizedSection(entry.section!!) + !mSectionStyleProvider.isMinimizedSection(entry.section!!) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt index 497691d18844..2227be120309 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt @@ -17,9 +17,9 @@ package com.android.systemui.statusbar.notification.collection.inflation import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.notification.SectionClassifier import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import javax.inject.Inject /** @@ -28,13 +28,13 @@ import javax.inject.Inject */ @SysUISingleton open class NotifUiAdjustmentProvider @Inject constructor( - private val sectionClassifier: SectionClassifier + private val sectionStyleProvider: SectionStyleProvider ) { private fun isEntryMinimized(entry: NotificationEntry): Boolean { val section = entry.section ?: error("Entry must have a section to determine if minimized") val parent = entry.parent ?: error("Entry must have a parent to determine if minimized") - val isMinimizedSection = sectionClassifier.isMinimizedSection(section) + val isMinimizedSection = sectionStyleProvider.isMinimizedSection(section) val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY val isGroupSummary = parent.summary == entry return isMinimizedSection && (isTopLevelEntry || isGroupSummary) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt index 68bdd18c9881..2148d3bb336a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification +package com.android.systemui.statusbar.notification.collection.provider import android.content.Context import com.android.systemui.dagger.SysUISingleton @@ -34,7 +34,7 @@ import javax.inject.Inject class SectionHeaderVisibilityProvider @Inject constructor( context: Context ) { - var neverShowSectionHeaders = context.resources.getBoolean(R.bool.config_notification_never_show_section_headers) - private set + val neverShowSectionHeaders = + context.resources.getBoolean(R.bool.config_notification_never_show_section_headers) var sectionHeadersVisible = true } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt index 1f2d0fe6c46e..50e7d1ce4ba0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification +package com.android.systemui.statusbar.notification.collection.provider import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection @@ -26,7 +26,7 @@ import javax.inject.Inject * NOTE: This class exists to avoid putting metadata like "isMinimized" on the NotifSection */ @SysUISingleton -class SectionClassifier @Inject constructor() { +class SectionStyleProvider @Inject constructor() { private lateinit var lowPrioritySections: Set<NotifSectioner> /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt index 6db544c77f87..75a71af7a0d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt @@ -16,12 +16,12 @@ package com.android.systemui.statusbar.notification.collection.render -import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection +import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.util.Compile import com.android.systemui.util.traceSection diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt index 6ed81078c3a4..51dc72848d9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt @@ -19,10 +19,10 @@ package com.android.systemui.statusbar.notification.collection.render import android.content.Context import android.view.View import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager -import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.util.traceSection import dagger.assisted.Assisted diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 415bd90ed23a..f3ee64ff05ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.graphics.Region; +import android.os.Handler; import android.util.Pools; import androidx.collection.ArraySet; @@ -29,6 +30,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.StatusBarState; @@ -104,8 +106,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, KeyguardBypassController bypassController, GroupMembershipManager groupMembershipManager, VisualStabilityProvider visualStabilityProvider, - ConfigurationController configurationController) { - super(context, logger); + ConfigurationController configurationController, + @Main Handler handler) { + super(context, logger, handler); Resources resources = mContext.getResources(); mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); statusBarStateController.addCallback(mStatusBarStateListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 7d96240d0b36..69beaf56519f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.hardware.biometrics.BiometricSourceType; import android.os.Handler; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -164,58 +165,66 @@ public class KeyguardBouncer { // In split system user mode, we never unlock system user. return; } - ensureView(); - mIsScrimmed = isScrimmed; - // On the keyguard, we want to show the bouncer when the user drags up, but it's - // not correct to end the falsing session. We still need to verify if those touches - // are valid. - // Later, at the end of the animation, when the bouncer is at the top of the screen, - // onFullyShown() will be called and FalsingManager will stop recording touches. - if (isScrimmed) { - setExpansion(EXPANSION_VISIBLE); - } + try { + Trace.beginSection("KeyguardBouncer#show"); - if (resetSecuritySelection) { - // showPrimarySecurityScreen() updates the current security method. This is needed in - // case we are already showing and the current security method changed. - showPrimarySecurityScreen(); - } + ensureView(); + mIsScrimmed = isScrimmed; - if (mContainer.getVisibility() == View.VISIBLE || mShowingSoon) { - return; - } + // On the keyguard, we want to show the bouncer when the user drags up, but it's + // not correct to end the falsing session. We still need to verify if those touches + // are valid. + // Later, at the end of the animation, when the bouncer is at the top of the screen, + // onFullyShown() will be called and FalsingManager will stop recording touches. + if (isScrimmed) { + setExpansion(EXPANSION_VISIBLE); + } + + if (resetSecuritySelection) { + // showPrimarySecurityScreen() updates the current security method. This is needed + // in case we are already showing and the current security method changed. + showPrimarySecurityScreen(); + } + + if (mContainer.getVisibility() == View.VISIBLE || mShowingSoon) { + return; + } - final int activeUserId = KeyguardUpdateMonitor.getCurrentUser(); - final boolean isSystemUser = + final int activeUserId = KeyguardUpdateMonitor.getCurrentUser(); + final boolean isSystemUser = UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM; - final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId; + final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId; - // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is - // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer. - if (allowDismissKeyguard && mKeyguardViewController.dismiss(activeUserId)) { - return; - } + // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) + // is set, this will dismiss the whole Keyguard. Otherwise, show the bouncer. + if (allowDismissKeyguard && mKeyguardViewController.dismiss(activeUserId)) { + return; + } - // This condition may indicate an error on Android, so log it. - if (!allowDismissKeyguard) { - Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId); - } + // This condition may indicate an error on Android, so log it. + if (!allowDismissKeyguard) { + Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + + keyguardUserId); + } - mShowingSoon = true; + mShowingSoon = true; - // Split up the work over multiple frames. - DejankUtils.removeCallbacks(mResetRunnable); - if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer() + // Split up the work over multiple frames. + DejankUtils.removeCallbacks(mResetRunnable); + if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer() && !mKeyguardUpdateMonitor.userNeedsStrongAuth() && !mKeyguardBypassController.getBypassEnabled()) { - mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); - } else { - DejankUtils.postAfterTraversal(mShowRunnable); - } + mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); + } else { + DejankUtils.postAfterTraversal(mShowRunnable); + } - mCallback.onBouncerVisiblityChanged(true /* shown */); - dispatchStartingToShow(); + mCallback.onBouncerVisiblityChanged(true /* shown */); + dispatchStartingToShow(); + } finally { + Trace.endSection(); + } } public boolean isScrimmed() { @@ -317,6 +326,7 @@ public class KeyguardBouncer { } public void hide(boolean destroyView) { + Trace.beginSection("KeyguardBouncer#hide"); if (isShowing()) { SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN); @@ -338,6 +348,7 @@ public class KeyguardBouncer { // be slow because of AM lock contention during unlocking. We can delay it a bit. mHandler.postDelayed(mRemoveViewRunnable, 50); } + Trace.endSection(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 56c74bf98e6b..24660b261c51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -288,7 +288,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW final boolean keyguardOrAod = state.mKeyguardShowing || (state.mDozing && mDozeParameters.getAlwaysOn()); if ((keyguardOrAod && !state.mBackdropShowing && !state.mLightRevealScrimOpaque) - || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) { + || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind()) { // Show the wallpaper if we're on keyguard/AOD and the wallpaper is not occluded by a // solid backdrop. Also, show it if we are currently animating between the // keyguard and the surface behind the keyguard - we want to use the wallpaper as a diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index 67de4e3b3c39..6b700a7f4519 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -38,7 +38,6 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation import com.android.systemui.util.leak.RotationUtils.getExactRotation import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation - import java.io.PrintWriter import java.lang.Math.max import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 49cd56f0176d..0f7949e29920 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -164,7 +164,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onVisibilityChanged(boolean isVisible) { if (!isVisible) { - cancelPostAuthActions(); mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN); } if (mAlternateAuthInterceptor != null) { @@ -502,42 +501,52 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void dismissWithAction(OnDismissAction r, Runnable cancelAction, boolean afterKeyguardGone, String message) { if (mShowing) { - cancelPendingWakeupAction(); - // If we're dozing, this needs to be delayed until after we wake up - unless we're - // wake-and-unlocking, because there dozing will last until the end of the transition. - if (mDozing && !isWakeAndUnlocking()) { - mPendingWakeupAction = new DismissWithActionRequest( - r, cancelAction, afterKeyguardGone, message); - return; - } + try { + Trace.beginSection("StatusBarKeyguardViewManager#dismissWithAction"); + cancelPendingWakeupAction(); + // If we're dozing, this needs to be delayed until after we wake up - unless we're + // wake-and-unlocking, because there dozing will last until the end of the + // transition. + if (mDozing && !isWakeAndUnlocking()) { + mPendingWakeupAction = new DismissWithActionRequest( + r, cancelAction, afterKeyguardGone, message); + return; + } - mAfterKeyguardGoneAction = r; - mKeyguardGoneCancelAction = cancelAction; - mDismissActionWillAnimateOnKeyguard = r != null && r.willRunAnimationOnKeyguard(); + mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; + mDismissActionWillAnimateOnKeyguard = r != null && r.willRunAnimationOnKeyguard(); + + // If there is an an alternate auth interceptor (like the UDFPS), show that one + // instead of the bouncer. + if (shouldShowAltAuth()) { + if (!afterKeyguardGone) { + mBouncer.setDismissAction(mAfterKeyguardGoneAction, + mKeyguardGoneCancelAction); + mAfterKeyguardGoneAction = null; + mKeyguardGoneCancelAction = null; + } + + updateAlternateAuthShowing( + mAlternateAuthInterceptor.showAlternateAuthBouncer()); + return; + } - // If there is an an alternate auth interceptor (like the UDFPS), show that one instead - // of the bouncer. - if (shouldShowAltAuth()) { - if (!afterKeyguardGone) { - mBouncer.setDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); + if (afterKeyguardGone) { + // we'll handle the dismiss action after keyguard is gone, so just show the + // bouncer + mBouncer.show(false /* resetSecuritySelection */); + } else { + // after authentication success, run dismiss action with the option to defer + // hiding the keyguard based on the return value of the OnDismissAction + mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, + mKeyguardGoneCancelAction); + // bouncer will handle the dismiss action, so we no longer need to track it here mAfterKeyguardGoneAction = null; mKeyguardGoneCancelAction = null; } - - updateAlternateAuthShowing(mAlternateAuthInterceptor.showAlternateAuthBouncer()); - return; - } - - if (afterKeyguardGone) { - // we'll handle the dismiss action after keyguard is gone, so just show the bouncer - mBouncer.show(false /* resetSecuritySelection */); - } else { - // after authentication success, run dismiss action with the option to defer - // hiding the keyguard based on the return value of the OnDismissAction - mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); - // bouncer will handle the dismiss action, so we no longer need to track it here - mAfterKeyguardGoneAction = null; - mKeyguardGoneCancelAction = null; + } finally { + Trace.endSection(); } } updateStates(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index bce5a159f79c..e5d5ed48cc9f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -24,6 +24,7 @@ import android.app.Notification; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.os.Handler; import android.provider.Settings; import android.util.ArrayMap; import android.view.accessibility.AccessibilityManager; @@ -34,6 +35,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.Dependency; import com.android.systemui.EventLogTags; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -79,8 +81,9 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } } - public HeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger) { - super(logger); + public HeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger, + @Main Handler handler) { + super(logger, handler); mContext = context; mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class); mUiEventLogger = Dependency.get(UiEventLogger.class); @@ -94,7 +97,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs); - ContentObserver settingsObserver = new ContentObserver(mHandler) { + ContentObserver settingsObserver = new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { final int packageSnoozeLengthMs = Settings.Global.getInt( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 006edcaf41de..ea423ce7020b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -264,8 +264,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene super.onEnd(animation); if (animation.getTypeMask() == WindowInsets.Type.ime()) { mEntry.mRemoteEditImeAnimatingAway = false; - mEntry.mRemoteEditImeVisible = - mEditText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()); + WindowInsets editTextRootWindowInsets = mEditText.getRootWindowInsets(); + if (editTextRootWindowInsets == null) { + Log.w(TAG, "onEnd called on detached view", new Exception()); + } + mEntry.mRemoteEditImeVisible = editTextRootWindowInsets != null + && editTextRootWindowInsets.isVisible(WindowInsets.Type.ime()); if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) { mController.removeRemoteInput(mEntry, mToken); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index a3f01c21d137..5eff8cdb635a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -45,6 +45,7 @@ import android.os.UserManager; import android.provider.Settings; import android.telephony.TelephonyCallback; import android.text.TextUtils; +import android.util.FeatureFlagUtils; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -63,6 +64,7 @@ import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.users.UserCreatingDialog; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dumpable; +import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; @@ -120,6 +122,8 @@ public class UserSwitcherController implements Dumpable { private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); @VisibleForTesting final GuestResumeSessionReceiver mGuestResumeSessionReceiver; + @VisibleForTesting + final GuestResetOrExitSessionReceiver mGuestResetOrExitSessionReceiver; private final KeyguardStateController mKeyguardStateController; private final DeviceProvisionedController mDeviceProvisionedController; private final DevicePolicyManager mDevicePolicyManager; @@ -185,7 +189,9 @@ public class UserSwitcherController implements Dumpable { InteractionJankMonitor interactionJankMonitor, LatencyTracker latencyTracker, DumpManager dumpManager, - DialogLaunchAnimator dialogLaunchAnimator) { + DialogLaunchAnimator dialogLaunchAnimator, + GuestResumeSessionReceiver guestResumeSessionReceiver, + GuestResetOrExitSessionReceiver guestResetOrExitSessionReceiver) { mContext = context; mActivityManager = activityManager; mUserTracker = userTracker; @@ -196,15 +202,14 @@ public class UserSwitcherController implements Dumpable { mFalsingManager = falsingManager; mInteractionJankMonitor = interactionJankMonitor; mLatencyTracker = latencyTracker; + mGuestResumeSessionReceiver = guestResumeSessionReceiver; + mGuestResetOrExitSessionReceiver = guestResetOrExitSessionReceiver; mGlobalSettings = globalSettings; - mGuestResumeSessionReceiver = new GuestResumeSessionReceiver( - this, mUserTracker, mUiEventLogger, secureSettings); mBgExecutor = bgExecutor; mLongRunningExecutor = longRunningExecutor; mUiExecutor = uiExecutor; - if (!UserManager.isGuestUserEphemeral()) { - mGuestResumeSessionReceiver.register(mBroadcastDispatcher); - } + mGuestResumeSessionReceiver.register(); + mGuestResetOrExitSessionReceiver.register(); mGuestUserAutoCreated = mContext.getResources().getBoolean( com.android.internal.R.bool.config_guestUserAutoCreated); mGuestIsResetting = new AtomicBoolean(); @@ -274,6 +279,10 @@ public class UserSwitcherController implements Dumpable { refreshUsers(UserHandle.USER_NULL); } + private static boolean isEnableGuestModeUxChanges(Context context) { + return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES); + } + /** * Refreshes users from UserManager. * @@ -520,20 +529,31 @@ public class UserSwitcherController implements Dumpable { private void onUserListItemClicked(int id, UserRecord record, DialogShower dialogShower) { int currUserId = mUserTracker.getUserId(); + // If switching from guest and guest is ephemeral, then follow the flow + // of showExitGuestDialog to remove current guest, + // and switch to selected user + UserInfo currUserInfo = mUserTracker.getUserInfo(); if (currUserId == id) { if (record.isGuest) { - showExitGuestDialog(id, dialogShower); + showExitGuestDialog(id, currUserInfo.isEphemeral(), dialogShower); } return; } - if (UserManager.isGuestUserEphemeral()) { - // If switching from guest, we want to bring up the guest exit dialog instead of switching - UserInfo currUserInfo = mUserManager.getUserInfo(currUserId); - if (currUserInfo != null && currUserInfo.isGuest()) { - showExitGuestDialog(currUserId, record.resolveId(), dialogShower); + + if (currUserInfo != null && currUserInfo.isGuest()) { + if (isEnableGuestModeUxChanges(mContext)) { + showExitGuestDialog(currUserId, currUserInfo.isEphemeral(), + record.resolveId(), dialogShower); return; + } else { + if (currUserInfo.isEphemeral()) { + showExitGuestDialog(currUserId, currUserInfo.isEphemeral(), + record.resolveId(), dialogShower); + return; + } } } + if (dialogShower != null) { // If we haven't morphed into another dialog, it means we have just switched users. // Then, dismiss the dialog. @@ -555,7 +575,7 @@ public class UserSwitcherController implements Dumpable { } } - private void showExitGuestDialog(int id, DialogShower dialogShower) { + private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) { int newId = UserHandle.USER_SYSTEM; if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); @@ -563,14 +583,15 @@ public class UserSwitcherController implements Dumpable { newId = info.id; } } - showExitGuestDialog(id, newId, dialogShower); + showExitGuestDialog(id, isGuestEphemeral, newId, dialogShower); } - private void showExitGuestDialog(int id, int targetId, DialogShower dialogShower) { + private void showExitGuestDialog(int id, boolean isGuestEphemeral, + int targetId, DialogShower dialogShower) { if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { mExitGuestDialog.cancel(); } - mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId); + mExitGuestDialog = new ExitGuestDialog(mContext, id, isGuestEphemeral, targetId); if (dialogShower != null) { dialogShower.showDialog(mExitGuestDialog); } else { @@ -803,6 +824,52 @@ public class UserSwitcherController implements Dumpable { } } + /** + * Exits guest user and switches to previous non-guest user. The guest must be the current + * user. + * + * @param guestUserId user id of the guest user to exit + * @param targetUserId user id of the guest user to exit, set to UserHandle.USER_NULL when + * target user id is not known + * @param forceRemoveGuestOnExit true: remove guest before switching user, + * false: remove guest only if its ephemeral, else keep guest + */ + public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId, + boolean forceRemoveGuestOnExit) { + UserInfo currentUser = mUserTracker.getUserInfo(); + if (currentUser.id != guestUserId) { + Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" + + " is not current user (" + currentUser.id + ")"); + return; + } + if (!currentUser.isGuest()) { + Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" + + " is not a guest"); + return; + } + + int newUserId = UserHandle.USER_SYSTEM; + if (targetUserId == UserHandle.USER_NULL) { + // when target user is not specified switch to last non guest user + if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { + UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); + if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { + newUserId = info.id; + } + } + } else { + newUserId = targetUserId; + } + + if (currentUser.isEphemeral() || forceRemoveGuestOnExit) { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); + removeGuestUser(currentUser.id, newUserId); + } else { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_SWITCH); + switchToUserId(newUserId); + } + } + private void scheduleGuestCreation() { if (!mGuestCreationScheduled.compareAndSet(false, true)) { return; @@ -970,9 +1037,14 @@ public class UserSwitcherController implements Dumpable { public String getName(Context context, UserRecord item) { if (item.isGuest) { if (item.isCurrent) { - return context.getString(mController.mGuestUserAutoCreated + if (isEnableGuestModeUxChanges(context)) { + return context.getString( + com.android.settingslib.R.string.guest_exit_quick_settings_button); + } else { + return context.getString(mController.mGuestUserAutoCreated ? com.android.settingslib.R.string.guest_reset_guest : com.android.settingslib.R.string.guest_exit_guest); + } } else { if (item.info != null) { return context.getString(com.android.internal.R.string.guest_name); @@ -989,8 +1061,13 @@ public class UserSwitcherController implements Dumpable { ? com.android.settingslib.R.string.guest_resetting : com.android.internal.R.string.guest_name); } else { - return context.getString( - com.android.settingslib.R.string.guest_new_guest); + if (isEnableGuestModeUxChanges(context)) { + // we always show "guest" as string, instead of "add guest" + return context.getString(com.android.internal.R.string.guest_name); + } else { + return context.getString( + com.android.settingslib.R.string.guest_new_guest); + } } } } @@ -1012,7 +1089,11 @@ public class UserSwitcherController implements Dumpable { protected static Drawable getIconDrawable(Context context, UserRecord item) { int iconRes; if (item.isAddUser) { - iconRes = R.drawable.ic_account_circle_filled; + if (isEnableGuestModeUxChanges(context)) { + iconRes = R.drawable.ic_add; + } else { + iconRes = R.drawable.ic_account_circle_filled; + } } else if (item.isGuest) { iconRes = R.drawable.ic_account_circle; } else if (item.isAddSupervisedUser) { @@ -1157,24 +1238,58 @@ public class UserSwitcherController implements Dumpable { private final int mGuestId; private final int mTargetId; + private final boolean mIsGuestEphemeral; - public ExitGuestDialog(Context context, int guestId, int targetId) { + ExitGuestDialog(Context context, int guestId, boolean isGuestEphemeral, + int targetId) { super(context); - setTitle(mGuestUserAutoCreated - ? com.android.settingslib.R.string.guest_reset_guest_dialog_title - : com.android.settingslib.R.string.guest_remove_guest_dialog_title); - setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); - setButton(DialogInterface.BUTTON_NEUTRAL, - context.getString(android.R.string.cancel), this); - setButton(DialogInterface.BUTTON_POSITIVE, - context.getString(mGuestUserAutoCreated + if (isEnableGuestModeUxChanges(context)) { + if (isGuestEphemeral) { + setTitle(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_title)); + setMessage(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_dialog_button), this); + } else { + setTitle(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_title_non_ephemeral)); + setMessage(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_message_non_ephemeral)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_NEGATIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_clear_data_button), + this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_save_data_button), + this); + } + } else { + setTitle(mGuestUserAutoCreated + ? com.android.settingslib.R.string.guest_reset_guest_dialog_title + : com.android.settingslib.R.string.guest_remove_guest_dialog_title); + setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString(mGuestUserAutoCreated ? com.android.settingslib.R.string.guest_reset_guest_confirm_button : com.android.settingslib.R.string.guest_remove_guest_confirm_button), - this); + this); + } SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing()); setCanceledOnTouchOutside(false); mGuestId = guestId; mTargetId = targetId; + mIsGuestEphemeral = isGuestEphemeral; } @Override @@ -1184,12 +1299,40 @@ public class UserSwitcherController implements Dumpable { if (mFalsingManager.isFalseTap(penalty)) { return; } - if (which == BUTTON_NEUTRAL) { - cancel(); + if (isEnableGuestModeUxChanges(getContext())) { + if (mIsGuestEphemeral) { + if (which == DialogInterface.BUTTON_POSITIVE) { + mDialogLaunchAnimator.dismissStack(this); + // Ephemeral guest: exit guest, guest is removed by the system + // on exit, since its marked ephemeral + exitGuestUser(mGuestId, mTargetId, false); + } else if (which == DialogInterface.BUTTON_NEGATIVE) { + // Cancel clicked, do nothing + cancel(); + } + } else { + if (which == DialogInterface.BUTTON_POSITIVE) { + mDialogLaunchAnimator.dismissStack(this); + // Non-ephemeral guest: exit guest, guest is not removed by the system + // on exit, since its marked non-ephemeral + exitGuestUser(mGuestId, mTargetId, false); + } else if (which == DialogInterface.BUTTON_NEGATIVE) { + mDialogLaunchAnimator.dismissStack(this); + // Non-ephemeral guest: remove guest and then exit + exitGuestUser(mGuestId, mTargetId, true); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + // Cancel clicked, do nothing + cancel(); + } + } } else { - mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); - mDialogLaunchAnimator.dismissStack(this); - removeGuestUser(mGuestId, mTargetId); + if (which == BUTTON_NEUTRAL) { + cancel(); + } else { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); + mDialogLaunchAnimator.dismissStack(this); + removeGuestUser(mGuestId, mTargetId); + } } } } @@ -1198,10 +1341,17 @@ public class UserSwitcherController implements Dumpable { final class AddUserDialog extends SystemUIDialog implements DialogInterface.OnClickListener { - public AddUserDialog(Context context) { + AddUserDialog(Context context) { super(context); + setTitle(com.android.settingslib.R.string.user_add_user_title); - setMessage(com.android.settingslib.R.string.user_add_user_message_short); + String message = context.getString( + com.android.settingslib.R.string.user_add_user_message_short); + UserInfo currentUser = mUserTracker.getUserInfo(); + if (currentUser != null && currentUser.isGuest() && currentUser.isEphemeral()) { + message += context.getString(R.string.user_add_user_message_guest_remove); + } + setMessage(message); setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(android.R.string.cancel), this); setButton(DialogInterface.BUTTON_POSITIVE, diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index b7f90a479518..c92491e58e00 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -169,7 +169,8 @@ public abstract class TvSystemUIModule { KeyguardBypassController bypassController, GroupMembershipManager groupManager, VisualStabilityProvider visualStabilityProvider, - ConfigurationController configurationController) { + ConfigurationController configurationController, + @Main Handler handler) { return new HeadsUpManagerPhone( context, headsUpManagerLogger, @@ -177,7 +178,8 @@ public abstract class TvSystemUIModule { bypassController, groupManager, visualStabilityProvider, - configurationController + configurationController, + handler ); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt index e42d537596c5..603cf3bcef74 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt @@ -18,6 +18,7 @@ package com.android.keyguard import android.graphics.Bitmap import android.graphics.Canvas +import android.graphics.Color import android.graphics.Typeface import android.graphics.fonts.Font import android.graphics.fonts.FontFamily @@ -194,6 +195,128 @@ class TextInterpolatorTest : SysuiTestCase() { assertThat(expected.sameAs(actual)).isTrue() } + + @Test + fun testGlyphCallback_Empty() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) + + val interp = TextInterpolator(layout).apply { + glyphFilter = { glyph, progress -> + } + } + interp.basePaint.set(START_PAINT) + interp.onBasePaintModified() + + interp.targetPaint.set(END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + assertThat(expected.sameAs(actual)).isTrue() + } + + @Test + fun testGlyphCallback_Xcoordinate() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) + + val interp = TextInterpolator(layout).apply { + glyphFilter = { glyph, progress -> + glyph.x += 30f + } + } + interp.basePaint.set(START_PAINT) + interp.onBasePaintModified() + + interp.targetPaint.set(END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + // The glyph position was modified by callback, so the bitmap should not be the same. + // We cannot modify the result of StaticLayout, so we cannot expect the exact bitmaps. + assertThat(expected.sameAs(actual)).isFalse() + } + + @Test + fun testGlyphCallback_Ycoordinate() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) + + val interp = TextInterpolator(layout).apply { + glyphFilter = { glyph, progress -> + glyph.y += 30f + } + } + interp.basePaint.set(START_PAINT) + interp.onBasePaintModified() + + interp.targetPaint.set(END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + // The glyph position was modified by callback, so the bitmap should not be the same. + // We cannot modify the result of StaticLayout, so we cannot expect the exact bitmaps. + assertThat(expected.sameAs(actual)).isFalse() + } + + @Test + fun testGlyphCallback_TextSize() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) + + val interp = TextInterpolator(layout).apply { + glyphFilter = { glyph, progress -> + glyph.textSize += 10f + } + } + interp.basePaint.set(START_PAINT) + interp.onBasePaintModified() + + interp.targetPaint.set(END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + // The glyph position was modified by callback, so the bitmap should not be the same. + // We cannot modify the result of StaticLayout, so we cannot expect the exact bitmaps. + assertThat(expected.sameAs(actual)).isFalse() + } + + @Test + fun testGlyphCallback_Color() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) + + val interp = TextInterpolator(layout).apply { + glyphFilter = { glyph, progress -> + glyph.color = Color.RED + } + } + interp.basePaint.set(START_PAINT) + interp.onBasePaintModified() + + interp.targetPaint.set(END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + // The glyph position was modified by callback, so the bitmap should not be the same. + // We cannot modify the result of StaticLayout, so we cannot expect the exact bitmaps. + assertThat(expected.sameAs(actual)).isFalse() + } } private fun Layout.toBitmap(width: Int, height: Int) = diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt index 6626bbe69706..b43856aae4cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt @@ -24,7 +24,11 @@ import android.test.suitebuilder.annotation.SmallTest import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.commandline.Command +import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.withArgCaptor @@ -37,10 +41,12 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyString +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.inOrder import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations import java.io.PrintWriter import java.io.Serializable @@ -56,16 +62,18 @@ import org.mockito.Mockito.`when` as whenever class FeatureFlagsDebugTest : SysuiTestCase() { private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug - @Mock private lateinit var mFlagManager: FlagManager - @Mock private lateinit var mMockContext: Context - @Mock private lateinit var mSecureSettings: SecureSettings - @Mock private lateinit var mSystemProperties: SystemPropertiesHelper - @Mock private lateinit var mResources: Resources - @Mock private lateinit var mDumpManager: DumpManager - @Mock private lateinit var mBarService: IStatusBarService - private val mFlagMap = mutableMapOf<Int, Flag<*>>() - private lateinit var mBroadcastReceiver: BroadcastReceiver - private lateinit var mClearCacheAction: Consumer<Int> + @Mock private lateinit var flagManager: FlagManager + @Mock private lateinit var mockContext: Context + @Mock private lateinit var secureSettings: SecureSettings + @Mock private lateinit var systemProperties: SystemPropertiesHelper + @Mock private lateinit var resources: Resources + @Mock private lateinit var dumpManager: DumpManager + @Mock private lateinit var commandRegistry: CommandRegistry + @Mock private lateinit var barService: IStatusBarService + @Mock private lateinit var pw: PrintWriter + private val flagMap = mutableMapOf<Int, Flag<*>>() + private lateinit var broadcastReceiver: BroadcastReceiver + private lateinit var clearCacheAction: Consumer<Int> private val teamfoodableFlagA = BooleanFlag(500, false, true) private val teamfoodableFlagB = BooleanFlag(501, true, true) @@ -73,34 +81,35 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - mFlagMap.put(teamfoodableFlagA.id, teamfoodableFlagA) - mFlagMap.put(teamfoodableFlagB.id, teamfoodableFlagB) + flagMap.put(teamfoodableFlagA.id, teamfoodableFlagA) + flagMap.put(teamfoodableFlagB.id, teamfoodableFlagB) mFeatureFlagsDebug = FeatureFlagsDebug( - mFlagManager, - mMockContext, - mSecureSettings, - mSystemProperties, - mResources, - mDumpManager, - mFlagMap, - mBarService + flagManager, + mockContext, + secureSettings, + systemProperties, + resources, + dumpManager, + flagMap, + commandRegistry, + barService ) - verify(mFlagManager).onSettingsChangedAction = any() - mBroadcastReceiver = withArgCaptor { - verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable(), + verify(flagManager).onSettingsChangedAction = any() + broadcastReceiver = withArgCaptor { + verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(), any()) } - mClearCacheAction = withArgCaptor { - verify(mFlagManager).clearCacheAction = capture() + clearCacheAction = withArgCaptor { + verify(flagManager).clearCacheAction = capture() } - whenever(mFlagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" } + whenever(flagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" } } @Test fun testReadBooleanFlag() { // Remember that the TEAMFOOD flag is id#1 and has special behavior. - whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true) - whenever(mFlagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false) + whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true) + whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false) assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue() assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue() assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse() @@ -109,7 +118,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testTeamFoodFlag_False() { - whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false) + whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false) assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse() assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue() @@ -120,7 +129,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testTeamFoodFlag_True() { - whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true) + whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true) assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue() assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue() @@ -131,11 +140,11 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testTeamFoodFlag_Overridden() { - whenever(mFlagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any())) + whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any())) .thenReturn(true) - whenever(mFlagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any())) + whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any())) .thenReturn(false) - whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true) + whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true) assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue() assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse() @@ -146,14 +155,14 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testReadResourceBooleanFlag() { - whenever(mResources.getBoolean(1001)).thenReturn(false) - whenever(mResources.getBoolean(1002)).thenReturn(true) - whenever(mResources.getBoolean(1003)).thenReturn(false) - whenever(mResources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() } - whenever(mResources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() } + whenever(resources.getBoolean(1001)).thenReturn(false) + whenever(resources.getBoolean(1002)).thenReturn(true) + whenever(resources.getBoolean(1003)).thenReturn(false) + whenever(resources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() } + whenever(resources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() } - whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true) - whenever(mFlagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false) + whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true) + whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false) assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse() assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue() @@ -171,7 +180,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testReadSysPropBooleanFlag() { - whenever(mSystemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer { + whenever(systemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer { if ("b".equals(it.getArgument<String?>(0))) { return@thenAnswer true } @@ -187,8 +196,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testReadStringFlag() { - whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo") - whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar") + whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo") + whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar") assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz") assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz") assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo") @@ -197,16 +206,16 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testReadResourceStringFlag() { - whenever(mResources.getString(1001)).thenReturn("") - whenever(mResources.getString(1002)).thenReturn("resource2") - whenever(mResources.getString(1003)).thenReturn("resource3") - whenever(mResources.getString(1004)).thenReturn(null) - whenever(mResources.getString(1005)).thenAnswer { throw NameNotFoundException() } - whenever(mResources.getString(1006)).thenAnswer { throw NameNotFoundException() } + whenever(resources.getString(1001)).thenReturn("") + whenever(resources.getString(1002)).thenReturn("resource2") + whenever(resources.getString(1003)).thenReturn("resource3") + whenever(resources.getString(1004)).thenReturn(null) + whenever(resources.getString(1005)).thenAnswer { throw NameNotFoundException() } + whenever(resources.getString(1006)).thenAnswer { throw NameNotFoundException() } - whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3") - whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4") - whenever(mFlagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6") + whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3") + whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4") + whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6") assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("") assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2") @@ -232,16 +241,16 @@ class FeatureFlagsDebugTest : SysuiTestCase() { addFlag(StringFlag(3, "flag3")) addFlag(ResourceStringFlag(4, 1004)) - mBroadcastReceiver.onReceive(mMockContext, null) - mBroadcastReceiver.onReceive(mMockContext, Intent()) - mBroadcastReceiver.onReceive(mMockContext, Intent("invalid action")) - mBroadcastReceiver.onReceive(mMockContext, Intent(FlagManager.ACTION_SET_FLAG)) + broadcastReceiver.onReceive(mockContext, null) + broadcastReceiver.onReceive(mockContext, Intent()) + broadcastReceiver.onReceive(mockContext, Intent("invalid action")) + broadcastReceiver.onReceive(mockContext, Intent(FlagManager.ACTION_SET_FLAG)) setByBroadcast(0, false) // unknown id does nothing setByBroadcast(1, "string") // wrong type does nothing setByBroadcast(2, 123) // wrong type does nothing setByBroadcast(3, false) // wrong type does nothing setByBroadcast(4, 123) // wrong type does nothing - verifyNoMoreInteractions(mFlagManager, mSecureSettings) + verifyNoMoreInteractions(flagManager, secureSettings) } @Test @@ -249,15 +258,15 @@ class FeatureFlagsDebugTest : SysuiTestCase() { addFlag(BooleanFlag(1, false)) // trying to erase an id not in the map does noting - mBroadcastReceiver.onReceive( - mMockContext, + broadcastReceiver.onReceive( + mockContext, Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0) ) - verifyNoMoreInteractions(mFlagManager, mSecureSettings) + verifyNoMoreInteractions(flagManager, secureSettings) // valid id with no value puts empty string in the setting - mBroadcastReceiver.onReceive( - mMockContext, + broadcastReceiver.onReceive( + mockContext, Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 1) ) verifyPutData(1, "", numReads = 0) @@ -298,48 +307,102 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Test fun testSetFlagClearsCache() { val flag1 = addFlag(StringFlag(1, "flag1")) - whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("original") + whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original") // gets the flag & cache it assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original") - verify(mFlagManager).readFlagValue(eq(1), eq(StringFlagSerializer)) + verify(flagManager).readFlagValue(eq(1), eq(StringFlagSerializer)) // hit the cache assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original") - verifyNoMoreInteractions(mFlagManager) + verifyNoMoreInteractions(flagManager) // set the flag setByBroadcast(1, "new") verifyPutData(1, "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2) - whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("new") + whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("new") assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("new") - verify(mFlagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer)) + verify(flagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer)) + } + + @Test + fun testRegisterCommand() { + verify(commandRegistry).registerCommand(anyString(), any()) + } + + @Test + fun testNoOpCommand() { + val cmd = captureCommand() + + cmd.execute(pw, ArrayList()) + verify(pw, atLeastOnce()).println() + verify(flagManager).readFlagValue<Boolean>(eq(1), any()) + verifyZeroInteractions(secureSettings) + } + + @Test + fun testReadFlagCommand() { + addFlag(BooleanFlag(1, false)) + val cmd = captureCommand() + cmd.execute(pw, listOf("1")) + verify(flagManager).readFlagValue<Boolean>(eq(1), any()) + } + + @Test + fun testSetFlagCommand() { + addFlag(BooleanFlag(1, false)) + val cmd = captureCommand() + cmd.execute(pw, listOf("1", "on")) + verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}") + } + + @Test + fun testToggleFlagCommand() { + addFlag(BooleanFlag(1, true)) + val cmd = captureCommand() + cmd.execute(pw, listOf("1", "toggle")) + verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2) + } + + @Test + fun testEraseFlagCommand() { + addFlag(BooleanFlag(1, true)) + val cmd = captureCommand() + cmd.execute(pw, listOf("1", "erase")) + verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt()) } private fun verifyPutData(id: Int, data: String, numReads: Int = 1) { - inOrder(mFlagManager, mSecureSettings).apply { - verify(mFlagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>()) - verify(mFlagManager).idToSettingsKey(eq(id)) - verify(mSecureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt()) - verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id), any()) + inOrder(flagManager, secureSettings).apply { + verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>()) + verify(flagManager).idToSettingsKey(eq(id)) + verify(secureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt()) + verify(flagManager).dispatchListenersAndMaybeRestart(eq(id), any()) }.verifyNoMoreInteractions() - verifyNoMoreInteractions(mFlagManager, mSecureSettings) + verifyNoMoreInteractions(flagManager, secureSettings) } private fun setByBroadcast(id: Int, value: Serializable?) { val intent = Intent(FlagManager.ACTION_SET_FLAG) intent.putExtra(FlagManager.EXTRA_ID, id) intent.putExtra(FlagManager.EXTRA_VALUE, value) - mBroadcastReceiver.onReceive(mMockContext, intent) + broadcastReceiver.onReceive(mockContext, intent) } private fun <F : Flag<*>> addFlag(flag: F): F { - val old = mFlagMap.put(flag.id, flag) + val old = flagMap.put(flag.id, flag) check(old == null) { "Flag ${flag.id} already registered" } return flag } + private fun captureCommand(): Command { + val captor = argumentCaptor<Function0<Command>>() + verify(commandRegistry).registerCommand(anyString(), capture(captor)) + + return captor.value.invoke() + } + @Test fun testDump() { val flag1 = BooleanFlag(1, true) @@ -350,10 +413,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() { val flag6 = ResourceStringFlag(6, 1006) val flag7 = ResourceStringFlag(7, 1007) - whenever(mResources.getBoolean(1002)).thenReturn(true) - whenever(mResources.getString(1006)).thenReturn("resource1006") - whenever(mResources.getString(1007)).thenReturn("resource1007") - whenever(mFlagManager.readFlagValue(eq(7), eq(StringFlagSerializer))) + whenever(resources.getBoolean(1002)).thenReturn(true) + whenever(resources.getString(1006)).thenReturn("resource1006") + whenever(resources.getString(1007)).thenReturn("resource1007") + whenever(flagManager.readFlagValue(eq(7), eq(StringFlagSerializer))) .thenReturn("override7") // WHEN the flags have been accessed diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index 8a388479c0e7..65d0adc99739 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -83,12 +83,10 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { private final class TestableAlertingNotificationManager extends AlertingNotificationManager { private AlertEntry mLastCreatedEntry; - private TestableAlertingNotificationManager() { - super(mock(HeadsUpManagerLogger.class)); + private TestableAlertingNotificationManager(Handler handler) { + super(mock(HeadsUpManagerLogger.class), handler); mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; - mHandler.removeCallbacksAndMessages(null); - mHandler = mTestHandler; } @Override @@ -109,8 +107,8 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { } } - protected AlertingNotificationManager createAlertingNotificationManager() { - return new TestableAlertingNotificationManager(); + protected AlertingNotificationManager createAlertingNotificationManager(Handler handler) { + return new TestableAlertingNotificationManager(handler); } protected StatusBarNotification createNewSbn(int id, Notification.Builder n) { @@ -144,7 +142,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { .build(); mEntry.setRow(mRow); - mAlertingNotificationManager = createAlertingNotificationManager(); + mAlertingNotificationManager = createAlertingNotificationManager(mTestHandler); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index a2a02cd939ef..30c01b5ba575 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -816,8 +816,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mExecutor.runAllReady(); reset(mRotateTextViewController); - // GIVEN keyguard is showing + // GIVEN keyguard is showing and not dozing when(mKeyguardStateController.isShowing()).thenReturn(true); + mController.setVisible(true); + mExecutor.runAllReady(); + reset(mRotateTextViewController); // WHEN keyguard showing changed called mKeyguardStateControllerCallback.onKeyguardShowingChanged(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java index 52bacd2360b3..d8a98d1e8ea4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java @@ -29,12 +29,12 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.policy.KeyguardStateController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java index 3b034f7af9a6..fc74f3954dc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java @@ -41,7 +41,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.RankingBuilder; -import com.android.systemui.statusbar.notification.SectionClassifier; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder; import com.android.systemui.statusbar.notification.collection.ListEntry; @@ -56,6 +55,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider; import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; @@ -96,9 +96,9 @@ public class PreparationCoordinatorTest extends SysuiTestCase { @Mock private IStatusBarService mService; @Mock private BindEventManagerImpl mBindEventManagerImpl; @Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater(); - private final SectionClassifier mSectionClassifier = new SectionClassifier(); + private final SectionStyleProvider mSectionStyleProvider = new SectionStyleProvider(); private final NotifUiAdjustmentProvider mAdjustmentProvider = - new NotifUiAdjustmentProvider(mSectionClassifier); + new NotifUiAdjustmentProvider(mSectionStyleProvider); @NonNull private NotificationEntryBuilder getNotificationEntryBuilder() { @@ -487,7 +487,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase { private static final int TEST_MAX_GROUP_DELAY = 100; private void setSectionIsLowPriority(boolean minimized) { - mSectionClassifier.setMinimizedSections(minimized + mSectionStyleProvider.setMinimizedSections(minimized ? Collections.singleton(mNotifSection.getSectioner()) : Collections.emptyList()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java index f4d8405a796e..ff08bc38c944 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java @@ -40,7 +40,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; -import com.android.systemui.statusbar.notification.SectionClassifier; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -48,6 +47,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider; import com.android.systemui.statusbar.notification.collection.render.NodeController; import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; @@ -68,7 +68,7 @@ public class RankingCoordinatorTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock private HighPriorityProvider mHighPriorityProvider; - @Mock private SectionClassifier mSectionClassifier; + @Mock private SectionStyleProvider mSectionStyleProvider; @Mock private NotifPipeline mNotifPipeline; @Mock private NodeController mAlertingHeaderController; @Mock private NodeController mSilentNodeController; @@ -92,7 +92,7 @@ public class RankingCoordinatorTest extends SysuiTestCase { mRankingCoordinator = new RankingCoordinator( mStatusBarStateController, mHighPriorityProvider, - mSectionClassifier, + mSectionStyleProvider, mAlertingHeaderController, mSilentHeaderController, mSilentNodeController); @@ -100,7 +100,7 @@ public class RankingCoordinatorTest extends SysuiTestCase { mEntry.setRanking(getRankingForUnfilteredNotif().build()); mRankingCoordinator.attach(mNotifPipeline); - verify(mSectionClassifier).setMinimizedSections(any()); + verify(mSectionStyleProvider).setMinimizedSections(any()); verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture()); mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0); mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt index 447ba1510e13..40859d0e6304 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt @@ -21,13 +21,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.FeedbackIcon -import com.android.systemui.statusbar.notification.SectionClassifier import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.collection.render.NotifRowController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -53,7 +53,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { @Mock private lateinit var pipeline: NotifPipeline @Mock private lateinit var assistantFeedbackController: AssistantFeedbackController - @Mock private lateinit var sectionClassifier: SectionClassifier + @Mock private lateinit var sectionStyleProvider: SectionStyleProvider @Mock private lateinit var section1: NotifSection @Mock private lateinit var section2: NotifSection @@ -66,7 +66,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { coordinator = RowAppearanceCoordinator( mContext, assistantFeedbackController, - sectionClassifier + sectionStyleProvider ) coordinator.attach(pipeline) beforeRenderListListener = withArgCaptor { @@ -82,8 +82,8 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { @Test fun testSetSystemExpandedOnlyOnFirst() { - whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false) - whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false) + whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(false) + whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(false) beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2)) afterRenderEntryListener.onAfterRenderEntry(entry1, controller1) verify(controller1).setSystemExpanded(eq(true)) @@ -93,8 +93,8 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { @Test fun testSetSystemExpandedNeverIfMinimized() { - whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true) - whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true) + whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(true) + whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(true) beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2)) afterRenderEntryListener.onAfterRenderEntry(entry1, controller1) verify(controller1).setSystemExpanded(eq(false)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt index 0e1865861cae..ff601938d544 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.collection.render import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager -import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.ListEntry @@ -28,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.getAttachState import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner +import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 1ecb09bc8514..3d57f66c5dc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -144,10 +144,9 @@ public class NotificationTestHelper { mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class), mock(VisualStabilityProvider.class), - mock(ConfigurationControllerImpl.class) + mock(ConfigurationControllerImpl.class), + new Handler(mTestLooper.getLooper()) ); - mHeadsUpManager.mHandler.removeCallbacksAndMessages(null); - mHeadsUpManager.mHandler = new Handler(mTestLooper.getLooper()); mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager); mIconManager = new IconManager( mock(CommonNotifCollection.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index db5741c90ebc..b8c8b5f26f0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.when; import android.content.Context; +import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; @@ -76,7 +77,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { VisualStabilityProvider visualStabilityProvider, StatusBarStateController statusBarStateController, KeyguardBypassController keyguardBypassController, - ConfigurationController configurationController + ConfigurationController configurationController, + Handler handler ) { super( context, @@ -85,7 +87,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { keyguardBypassController, groupManager, visualStabilityProvider, - configurationController + configurationController, + handler ); mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; @@ -105,6 +108,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { when(mVSProvider.isReorderingAllowed()).thenReturn(true); mDependency.injectMockDependency(NotificationShadeWindowController.class); mDependency.injectMockDependency(ConfigurationController.class); + super.setUp(); + mHeadsUpManager = new TestableHeadsUpManagerPhone( mContext, mHeadsUpManagerLogger, @@ -112,11 +117,9 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { mVSProvider, mStatusBarStateController, mBypassController, - mConfigurationController + mConfigurationController, + mTestHandler ); - super.setUp(); - mHeadsUpManager.mHandler.removeCallbacksAndMessages(null); - mHeadsUpManager.mHandler = mTestHandler; } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java index 7070bc19db62..56dfb0cee520 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; +import android.os.Handler; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -51,7 +52,6 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.wm.shell.bubbles.Bubbles; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -89,7 +89,8 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); - mHeadsUpManager = new HeadsUpManager(mContext, mock(HeadsUpManagerLogger.class)) {}; + mHeadsUpManager = new HeadsUpManager(mContext, mock(HeadsUpManagerLogger.class), + mock(Handler.class)) {}; when(mNotificationEntryManager.getPendingNotificationsIterator()) .thenReturn(mPendingEntries.values()); @@ -114,11 +115,6 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { mHeadsUpManager.addListener(mGroupAlertTransferHelper); } - @After - public void tearDown() { - mHeadsUpManager.mHandler.removeCallbacksAndMessages(null); - } - private void mockHasHeadsUpContentView(NotificationEntry entry, boolean hasHeadsUpContentView) { RowContentBindParams params = new RowContentBindParams(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index 424a40058997..f39d6875cffc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -36,6 +36,7 @@ import android.app.PendingIntent; import android.app.Person; import android.content.Context; import android.content.Intent; +import android.os.Handler; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -73,8 +74,8 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Mock private HeadsUpManagerLogger mLogger; private final class TestableHeadsUpManager extends HeadsUpManager { - TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger) { - super(context, logger); + TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger, Handler handler) { + super(context, logger, handler); mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; } @@ -91,10 +92,9 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { mDependency.injectTestDependency(UiEventLogger.class, mUiEventLoggerFake); when(mEntry.getSbn()).thenReturn(mSbn); when(mSbn.getNotification()).thenReturn(mNotification); - mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger); + super.setUp(); - mHeadsUpManager.mHandler.removeCallbacksAndMessages(null); - mHeadsUpManager.mHandler = mTestHandler; + mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger, mTestHandler); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index e3d2a2951c97..152815f2d47e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.app.IActivityManager +import android.app.NotificationManager import android.app.admin.DevicePolicyManager import android.content.Context import android.content.DialogInterface @@ -37,7 +38,9 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.util.LatencyTracker import com.android.internal.util.UserIcons +import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver +import com.android.systemui.GuestSessionNotification import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator @@ -99,6 +102,11 @@ class UserSwitcherControllerTest : SysuiTestCase() { @Mock private lateinit var threadedRenderer: ThreadedRenderer @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator @Mock private lateinit var globalSettings: GlobalSettings + @Mock private lateinit var guestSessionNotification: GuestSessionNotification + @Mock private lateinit var guestResetOrExitSessionReceiver: GuestResetOrExitSessionReceiver + private lateinit var resetSessionDialogFactory: + GuestResumeSessionReceiver.ResetSessionDialog.Factory + private lateinit var guestResumeSessionReceiver: GuestResumeSessionReceiver private lateinit var testableLooper: TestableLooper private lateinit var bgExecutor: FakeExecutor private lateinit var longRunningExecutor: FakeExecutor @@ -130,9 +138,28 @@ class UserSwitcherControllerTest : SysuiTestCase() { com.android.internal.R.bool.config_guestUserAutoCreated, false) mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java)) + mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, + mock(NotificationManager::class.java)) mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager::class.java)) + resetSessionDialogFactory = object : GuestResumeSessionReceiver.ResetSessionDialog.Factory { + override fun create(userId: Int): GuestResumeSessionReceiver.ResetSessionDialog { + return GuestResumeSessionReceiver.ResetSessionDialog( + mContext, + mock(UserSwitcherController::class.java), + uiEventLogger, + userId + ) + } + } + + guestResumeSessionReceiver = GuestResumeSessionReceiver(userTracker, + secureSettings, + broadcastDispatcher, + guestSessionNotification, + resetSessionDialogFactory) + `when`(userManager.canAddMoreUsers(eq(UserManager.USER_TYPE_FULL_SECONDARY))) .thenReturn(true) `when`(notificationShadeWindowView.context).thenReturn(context) @@ -195,7 +222,9 @@ class UserSwitcherControllerTest : SysuiTestCase() { interactionJankMonitor, latencyTracker, dumpManager, - dialogLaunchAnimator) + dialogLaunchAnimator, + guestResumeSessionReceiver, + guestResetOrExitSessionReceiver) userSwitcherController.init(notificationShadeWindowView) } @@ -285,7 +314,10 @@ class UserSwitcherControllerTest : SysuiTestCase() { .getButton(DialogInterface.BUTTON_POSITIVE).performClick() testableLooper.processAllMessages() assertEquals(1, uiEventLogger.numLogs()) - assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0)) + assertTrue( + QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id == uiEventLogger.eventId(0) || + QSUserSwitcherEvent.QS_USER_SWITCH.id == uiEventLogger.eventId(0) + ) } @Test @@ -347,7 +379,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null) assertNotNull(userSwitcherController.mExitGuestDialog) userSwitcherController.mExitGuestDialog - .getButton(DialogInterface.BUTTON_NEGATIVE).performClick() + .getButton(DialogInterface.BUTTON_NEUTRAL).performClick() testableLooper.processAllMessages() assertEquals(0, uiEventLogger.numLogs()) } diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index dfa34bb50805..4ca83dde8b30 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -286,6 +286,11 @@ message SystemMessage { // Package: android NOTE_MTE_OVERRIDE_ENABLED = 69; + // Notify the user that this is a guest session with information + // about first login and ephemeral state + // Package: android + NOTE_GUEST_SESSION = 70; + // Inform the user of notification permissions changes. // Package: android NOTE_REVIEW_NOTIFICATION_PERMISSIONS = 71; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 2cf0e3e54326..9a257e54cf41 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -45,6 +45,7 @@ import android.content.Context; import android.graphics.Region; import android.os.Handler; import android.util.DisplayMetrics; +import android.util.Log; import android.util.Slog; import android.view.Display; import android.view.InputDevice; @@ -84,12 +85,14 @@ import java.util.List; public class TouchExplorer extends BaseEventStreamTransformation implements GestureManifold.Listener { - static final boolean DEBUG = false; private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER; // Tag for logging received events. private static final String LOG_TAG = "TouchExplorer"; + // To enable these logs, run: 'adb shell setprop log.tag.TouchExplorer DEBUG' (requires restart) + static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); + // The maximum of the cosine between the vectors of two moving // pointers so they can be considered moving in the same direction. private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java new file mode 100644 index 000000000000..715697d82cad --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2021 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.autofill; + +import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; + +import static com.android.server.autofill.Helper.sVerbose; + +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.ICancellationSignal; +import android.os.RemoteException; +import android.service.autofill.Dataset; +import android.service.autofill.FillResponse; +import android.service.autofill.IFillCallback; +import android.service.autofill.SaveInfo; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.util.Slog; +import android.view.autofill.AutofillId; +import android.view.autofill.IAutoFillManagerClient; +import android.view.inputmethod.InlineSuggestionsRequest; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.infra.AndroidFuture; + +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Maintains a client suggestions session with the + * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}. + * + */ +final class ClientSuggestionsSession { + + private static final String TAG = "ClientSuggestionsSession"; + private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS; + + private final int mSessionId; + private final IAutoFillManagerClient mClient; + private final Handler mHandler; + private final ComponentName mComponentName; + + private final RemoteFillService.FillServiceCallbacks mCallbacks; + + private final Object mLock = new Object(); + @GuardedBy("mLock") + private AndroidFuture<FillResponse> mPendingFillRequest; + @GuardedBy("mLock") + private int mPendingFillRequestId = INVALID_REQUEST_ID; + + ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler, + ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) { + mSessionId = sessionId; + mClient = client; + mHandler = handler; + mComponentName = componentName; + mCallbacks = callbacks; + } + + void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) { + final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); + final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>(); + final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>(); + + mHandler.post(() -> { + if (sVerbose) { + Slog.v(TAG, "calling onFillRequest() for id=" + requestId); + } + + try { + mClient.requestFillFromClient(requestId, inlineRequest, + new FillCallbackImpl(fillRequest, futureRef, cancellationSink)); + } catch (RemoteException e) { + fillRequest.completeExceptionally(e); + } + }); + + fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS); + futureRef.set(fillRequest); + + synchronized (mLock) { + mPendingFillRequest = fillRequest; + mPendingFillRequestId = requestId; + } + + fillRequest.whenComplete((res, err) -> mHandler.post(() -> { + synchronized (mLock) { + mPendingFillRequest = null; + mPendingFillRequestId = INVALID_REQUEST_ID; + } + if (err == null) { + processAutofillId(res); + mCallbacks.onFillRequestSuccess(requestId, res, + mComponentName.getPackageName(), flags); + } else { + Slog.e(TAG, "Error calling on client fill request", err); + if (err instanceof TimeoutException) { + dispatchCancellationSignal(cancellationSink.get()); + mCallbacks.onFillRequestTimeout(requestId); + } else if (err instanceof CancellationException) { + dispatchCancellationSignal(cancellationSink.get()); + } else { + mCallbacks.onFillRequestFailure(requestId, err.getMessage()); + } + } + })); + } + + /** + * Gets the application info for the component. + */ + @Nullable + static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) { + try { + ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo( + comp.getPackageName(), + PackageManager.GET_META_DATA, + userId); + if (si != null) { + return si; + } + } catch (RemoteException e) { + } + return null; + } + + /** + * Gets the user-visible name of the application. + */ + @Nullable + @GuardedBy("mLock") + static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) { + return appInfo == null ? null : appInfo.loadSafeLabel( + context.getPackageManager(), 0 /* do not ellipsize */, + TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM); + } + + /** + * Gets the user-visible icon of the application. + */ + @Nullable + @GuardedBy("mLock") + static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) { + return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager()); + } + + int cancelCurrentRequest() { + synchronized (mLock) { + return mPendingFillRequest != null && mPendingFillRequest.cancel(false) + ? mPendingFillRequestId + : INVALID_REQUEST_ID; + } + } + + /** + * The {@link AutofillId} which the client gets from its view is not contain the session id, + * but Autofill framework is using the {@link AutofillId} with a session id. So before using + * those ids in the Autofill framework, applies the current session id. + * + * @param res which response need to apply for a session id + */ + private void processAutofillId(FillResponse res) { + if (res == null) { + return; + } + + final List<Dataset> datasets = res.getDatasets(); + if (datasets != null && !datasets.isEmpty()) { + for (int i = 0; i < datasets.size(); i++) { + final Dataset dataset = datasets.get(i); + if (dataset != null) { + applySessionId(dataset.getFieldIds()); + } + } + } + + final SaveInfo saveInfo = res.getSaveInfo(); + if (saveInfo != null) { + applySessionId(saveInfo.getOptionalIds()); + applySessionId(saveInfo.getRequiredIds()); + applySessionId(saveInfo.getSanitizerValues()); + applySessionId(saveInfo.getTriggerId()); + } + } + + private void applySessionId(List<AutofillId> ids) { + if (ids == null || ids.isEmpty()) { + return; + } + + for (int i = 0; i < ids.size(); i++) { + applySessionId(ids.get(i)); + } + } + + private void applySessionId(AutofillId[][] ids) { + if (ids == null) { + return; + } + for (int i = 0; i < ids.length; i++) { + applySessionId(ids[i]); + } + } + + private void applySessionId(AutofillId[] ids) { + if (ids == null) { + return; + } + for (int i = 0; i < ids.length; i++) { + applySessionId(ids[i]); + } + } + + private void applySessionId(AutofillId id) { + if (id == null) { + return; + } + id.setSessionId(mSessionId); + } + + private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) { + if (signal == null) { + return; + } + try { + signal.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Error requesting a cancellation", e); + } + } + + private class FillCallbackImpl extends IFillCallback.Stub { + final AndroidFuture<FillResponse> mFillRequest; + final AtomicReference<AndroidFuture<FillResponse>> mFutureRef; + final AtomicReference<ICancellationSignal> mCancellationSink; + + FillCallbackImpl(AndroidFuture<FillResponse> fillRequest, + AtomicReference<AndroidFuture<FillResponse>> futureRef, + AtomicReference<ICancellationSignal> cancellationSink) { + mFillRequest = fillRequest; + mFutureRef = futureRef; + mCancellationSink = cancellationSink; + } + + @Override + public void onCancellable(ICancellationSignal cancellation) { + AndroidFuture<FillResponse> future = mFutureRef.get(); + if (future != null && future.isCancelled()) { + dispatchCancellationSignal(cancellation); + } else { + mCancellationSink.set(cancellation); + } + } + + @Override + public void onSuccess(FillResponse response) { + mFillRequest.complete(response); + } + + @Override + public void onFailure(int requestId, CharSequence message) { + String errorMessage = message == null ? "" : String.valueOf(message); + mFillRequest.completeExceptionally( + new RuntimeException(errorMessage)); + } + } +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 7e277ba3c0d0..b0445ae6d886 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -31,6 +31,7 @@ import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; +import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; @@ -61,6 +62,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.pm.ApplicationInfo; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -364,6 +366,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl(); + @Nullable + private ClientSuggestionsSession mClientSuggestionsSession; + // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a // new one per Session. private final BroadcastReceiver mDelayedFillBroadcastReceiver = @@ -456,6 +461,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Whether the current {@link FillResponse} is expired. */ private boolean mExpiredResponse; + /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */ + private boolean mClientSuggestionsEnabled; + /** Whether the fill dialog UI is disabled. */ private boolean mFillDialogDisabled; } @@ -486,14 +494,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mWaitForInlineRequest = inlineSuggestionsRequest != null; mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; - maybeRequestFillLocked(); + mWaitForInlineRequest = inlineSuggestionsRequest != null; + maybeRequestFillFromServiceLocked(); viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } : null; } + void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) { + mPendingFillRequest = null; + mWaitForInlineRequest = inlineRequest != null; + mPendingInlineSuggestionsRequest = inlineRequest; + } + @GuardedBy("mLock") - void maybeRequestFillLocked() { + void maybeRequestFillFromServiceLocked() { if (mPendingFillRequest == null) { return; } @@ -503,10 +518,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), - mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), - mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest, - mPendingFillRequest.getDelayedFillIntentSender()); + if (mPendingInlineSuggestionsRequest.isServiceSupported()) { + mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), + mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getClientState(), + mPendingFillRequest.getFlags(), + mPendingInlineSuggestionsRequest, + mPendingFillRequest.getDelayedFillIntentSender()); + } } mLastFillRequest = mPendingFillRequest; @@ -618,7 +637,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState : mDelayedFillPendingIntent.getIntentSender()); mPendingFillRequest = request; - maybeRequestFillLocked(); + maybeRequestFillFromServiceLocked(); } if (mActivityToken != null) { @@ -843,30 +862,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Cancels the last request sent to the {@link #mRemoteFillService}. + * Cancels the last request sent to the {@link #mRemoteFillService} or the + * {@link #mClientSuggestionsSession}. */ @GuardedBy("mLock") private void cancelCurrentRequestLocked() { - if (mRemoteFillService == null) { - wtf(null, "cancelCurrentRequestLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + if (mRemoteFillService == null && mClientSuggestionsSession == null) { + wtf(null, "cancelCurrentRequestLocked() called without a remote service or a " + + "client suggestions session. mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); return; } - final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); - // Remove the FillContext as there will never be a response for the service - if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) { - final int numContexts = mContexts.size(); + if (mRemoteFillService != null) { + final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); - // It is most likely the last context, hence search backwards - for (int i = numContexts - 1; i >= 0; i--) { - if (mContexts.get(i).getRequestId() == canceledRequest) { - if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest); - mContexts.remove(i); - break; + // Remove the FillContext as there will never be a response for the service + if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) { + final int numContexts = mContexts.size(); + + // It is most likely the last context, hence search backwards + for (int i = numContexts - 1; i >= 0; i--) { + if (mContexts.get(i).getRequestId() == canceledRequest) { + if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest); + mContexts.remove(i); + break; + } } } } + + if (mClientSuggestionsSession != null) { + mClientSuggestionsSession.cancelCurrentRequest(); + } } private boolean isViewFocusedLocked(int flags) { @@ -931,17 +959,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // structure is taken. This causes only one fill request per burst of focus changes. cancelCurrentRequestLocked(); - // Only ask IME to create inline suggestions request if Autofill provider supports it and - // the render service is available except the autofill is triggered manually and the view - // is also not focused. + // Only ask IME to create inline suggestions request when + // 1. Autofill provider supports it or client enabled client suggestions. + // 2. The render service is available. + // 3. The view is focused. (The view may not be focused if the autofill is triggered + // manually.) final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); - if (mSessionFlags.mInlineSupportedByService + if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled) && remoteRenderService != null - && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { - Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = - mAssistReceiver.newAutofillRequestLocked(viewState, - /* isInlineRequest= */ true); + && (isViewFocusedLocked(flags) || (isRequestSupportFillDialog(flags)))) { + final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer; + if (mSessionFlags.mClientSuggestionsEnabled) { + final int finalRequestId = requestId; + inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> { + // Using client suggestions + synchronized (mLock) { + onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest); + } + viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); + }; + } else { + inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked( + viewState, /* isInlineRequest= */ true); + } if (inlineSuggestionsRequestConsumer != null) { final AutofillId focusedId = mCurrentViewId; final int requestIdCopy = requestId; @@ -957,10 +998,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ); viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } + } else if (mSessionFlags.mClientSuggestionsEnabled) { + // Request client suggestions for the dropdown mode + onClientFillRequestLocked(requestId, null); } else { mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false); } + if (mSessionFlags.mClientSuggestionsEnabled) { + // Using client suggestions, unnecessary request AssistStructure + return; + } + // Now request the assist structure data. requestAssistStructureLocked(requestId, flags); } @@ -1020,10 +1069,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName = componentName; mCompatMode = compatMode; mSessionState = STATE_ACTIVE; + synchronized (mLock) { mSessionFlags = new SessionFlags(); mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly; mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked(); + mSessionFlags.mClientSuggestionsEnabled = + (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0; setClientLocked(client); } @@ -1135,12 +1187,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestLog != null) { requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1); } - processNullResponseLocked(requestId, requestFlags); + processNullResponseOrFallbackLocked(requestId, requestFlags); return; } fieldClassificationIds = response.getFieldClassificationIds(); - if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { + if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null + && !mService.isFieldClassificationEnabledLocked()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestId, requestFlags); return; @@ -1225,6 +1278,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + @GuardedBy("mLock") + private void processNullResponseOrFallbackLocked(int requestId, int flags) { + if (!mSessionFlags.mClientSuggestionsEnabled) { + processNullResponseLocked(requestId, flags); + return; + } + + // fallback to the default platform password manager + mSessionFlags.mClientSuggestionsEnabled = false; + + final InlineSuggestionsRequest inlineRequest = + (mLastInlineSuggestionsRequest != null + && mLastInlineSuggestionsRequest.first == requestId) + ? mLastInlineSuggestionsRequest.second : null; + mAssistReceiver.newAutofillRequestLocked(inlineRequest); + requestAssistStructureLocked(requestId, + flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS); + return; + } + // FillServiceCallbacks @Override public void onFillRequestFailure(int requestId, @Nullable CharSequence message) { @@ -3196,13 +3269,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState filterText = value.getTextValue().toString(); } - final CharSequence serviceLabel; - final Drawable serviceIcon; + final CharSequence targetLabel; + final Drawable targetIcon; synchronized (mLock) { - serviceLabel = mService.getServiceLabelLocked(); - serviceIcon = mService.getServiceIconLocked(); + if (mSessionFlags.mClientSuggestionsEnabled) { + final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName, + mService.getUserId()); + targetLabel = ClientSuggestionsSession.getAppLabelLocked( + mService.getMaster().getContext(), appInfo); + targetIcon = ClientSuggestionsSession.getAppIconLocked( + mService.getMaster().getContext(), appInfo); + } else { + targetLabel = mService.getServiceLabelLocked(); + targetIcon = mService.getServiceIconLocked(); + } } - if (serviceLabel == null || serviceIcon == null) { + if (targetLabel == null || targetIcon == null) { wtf(null, "onFillReady(): no service label or icon"); return; } @@ -3233,7 +3315,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillUi(filledId, response, filterText, mService.getServicePackageName(), mComponentName, - serviceLabel, serviceIcon, this, id, mCompatMode); + targetLabel, targetIcon, this, id, mCompatMode); synchronized (mLock) { mService.logDatasetShown(id, mClientState, UI_TYPE_MENU); @@ -3362,6 +3444,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } + final InlineSuggestionsRequest request = inlineSuggestionsRequest.get(); + if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported() + || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) { + if (sDebug) { + Slog.d(TAG, "Inline suggestions not supported for " + + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service") + + ". Falling back to dropdown."); + } + return false; + } + final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if (remoteRenderService == null) { @@ -3370,7 +3463,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = - new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, + new InlineFillUi.InlineFillUiInfo(request, focusedId, filterText, remoteRenderService, userId, id); InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, new InlineFillUi.InlineSuggestionUiCallback() { @@ -3980,6 +4073,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + @GuardedBy("mLock") + private void onClientFillRequestLocked(int requestId, + InlineSuggestionsRequest inlineSuggestionsRequest) { + if (mClientSuggestionsSession == null) { + mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler, + mComponentName, this); + } + + if (mContexts == null) { + mContexts = new ArrayList<>(1); + } + + if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) { + inlineSuggestionsRequest = null; + } + + mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags); + } + /** * The result of checking whether to show the save dialog, when session can be saved. * diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java index e80a6d9e0907..9f0deea503cf 100644 --- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java @@ -235,60 +235,7 @@ public class PackageManagerBackupAgent extends BackupAgent { return; } - long homeVersion = 0; - ArrayList<byte[]> homeSigHashes = null; - PackageInfo homeInfo = null; - String homeInstaller = null; - ComponentName home = getPreferredHomeComponent(); - if (home != null) { - try { - homeInfo = mPackageManager.getPackageInfoAsUser(home.getPackageName(), - PackageManager.GET_SIGNING_CERTIFICATES, mUserId); - homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName()); - homeVersion = homeInfo.getLongVersionCode(); - SigningInfo signingInfo = homeInfo.signingInfo; - if (signingInfo == null) { - Slog.e(TAG, "Home app has no signing information"); - } else { - // retrieve the newest sigs to back up - // TODO (b/73988180) use entire signing history in case of rollbacks - Signature[] homeInfoSignatures = signingInfo.getApkContentsSigners(); - homeSigHashes = BackupUtils.hashSignatureArray(homeInfoSignatures); - } - } catch (NameNotFoundException e) { - Slog.w(TAG, "Can't access preferred home info"); - // proceed as though there were no preferred home set - home = null; - } - } - try { - // We need to push a new preferred-home-app record if: - // 1. the version of the home app has changed since our last backup; - // 2. the home app [or absence] we now use differs from the prior state, - // OR 3. it looks like we use the same home app + version as before, but - // the signatures don't match so we treat them as different apps. - PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); - final boolean needHomeBackup = (homeVersion != mStoredHomeVersion) - || !Objects.equals(home, mStoredHomeComponent) - || (home != null - && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo, pmi)); - if (needHomeBackup) { - if (DEBUG) { - Slog.i(TAG, "Home preference changed; backing up new state " + home); - } - if (home != null) { - outputBuffer.reset(); - outputBufferStream.writeUTF(home.flattenToString()); - outputBufferStream.writeLong(homeVersion); - outputBufferStream.writeUTF(homeInstaller != null ? homeInstaller : "" ); - writeSignatureHashArray(outputBufferStream, homeSigHashes); - writeEntity(data, DEFAULT_HOME_KEY, outputBuffer.toByteArray()); - } else { - data.writeEntityHeader(DEFAULT_HOME_KEY, -1); - } - } - /* * Global metadata: * @@ -403,7 +350,7 @@ public class PackageManagerBackupAgent extends BackupAgent { } // Finally, write the new state blob -- just the list of all apps we handled - writeStateFile(mAllPackages, home, homeVersion, homeSigHashes, newState); + writeStateFile(mAllPackages, newState); } private static void writeEntity(BackupDataOutput data, String key, byte[] bytes) @@ -623,8 +570,7 @@ public class PackageManagerBackupAgent extends BackupAgent { } // Util: write out our new backup state file - private void writeStateFile(List<PackageInfo> pkgs, ComponentName preferredHome, - long homeVersion, ArrayList<byte[]> homeSigHashes, ParcelFileDescriptor stateFile) { + private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); BufferedOutputStream outbuf = new BufferedOutputStream(outstream); DataOutputStream out = new DataOutputStream(outbuf); @@ -635,14 +581,6 @@ public class PackageManagerBackupAgent extends BackupAgent { out.writeUTF(STATE_FILE_HEADER); out.writeInt(STATE_FILE_VERSION); - // If we remembered a preferred home app, record that - if (preferredHome != null) { - out.writeUTF(DEFAULT_HOME_KEY); - out.writeUTF(preferredHome.flattenToString()); - out.writeLong(homeVersion); - writeSignatureHashArray(out, homeSigHashes); - } - // Conclude with the metadata block out.writeUTF(GLOBAL_METADATA_KEY); out.writeInt(Build.VERSION.SDK_INT); @@ -789,6 +727,8 @@ public class PackageManagerBackupAgent extends BackupAgent { + Build.VERSION.INCREMENTAL + ")"); } } else if (key.equals(DEFAULT_HOME_KEY)) { + // Default home app data is no longer backed up by this agent. This code is + // kept to handle restore of old backups that still contain home app data. String cn = inputBufferStream.readUTF(); mRestoredHome = ComponentName.unflattenFromString(cn); mRestoredHomeVersion = inputBufferStream.readLong(); diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 1af35af9fc17..e886ed084e7f 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -85,7 +85,6 @@ import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.SELinux; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; @@ -3986,7 +3985,7 @@ public class UserBackupManagerService { String callerLogString = "BMS.filterAppsEligibleForBackup"; TransportConnection transportConnection = mTransportManager.getCurrentTransportClient(callerLogString); - List<String> eligibleApps = new LinkedList<>(); + List<String> eligibleApps = new ArrayList<>(); for (String packageName : packages) { if (mScheduledBackupEligibility.appIsRunningAndEligibleForBackupWithTransport( transportConnection, packageName)) { @@ -3996,7 +3995,7 @@ public class UserBackupManagerService { if (transportConnection != null) { mTransportManager.disposeOfTransportClient(transportConnection, callerLogString); } - return eligibleApps.toArray(new String[eligibleApps.size()]); + return eligibleApps.toArray(new String[0]); } finally { Binder.restoreCallingIdentity(oldToken); } @@ -4144,6 +4143,24 @@ public class UserBackupManagerService { pw.print(" : "); pw.println(entry.packageName); } + pw.println(userPrefix + "Agent timeouts:"); + pw.println(" KvBackupAgentTimeoutMillis: " + + mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis()); + pw.println(" FullBackupAgentTimeoutMillis: " + + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis()); + pw.println(" SharedBackupAgentTimeoutMillis: " + + mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis()); + pw.println(" RestoreAgentTimeoutMillis (system): " + + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis( + Process.FIRST_APPLICATION_UID - 1)); + pw.println(" RestoreAgentTimeoutMillis: " + + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis( + Process.FIRST_APPLICATION_UID)); + pw.println(" RestoreAgentFinishedTimeoutMillis: " + + mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis()); + pw.println(" QuotaExceededTimeoutMillis: " + + mAgentTimeoutParameters.getQuotaExceededTimeoutMillis()); + } } diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index 76df8b9f84e8..e78c8d1ddcac 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -24,8 +24,10 @@ import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; +import android.annotation.NonNull; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; +import android.app.backup.BackupAgent; import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.IBackupManagerMonitor; @@ -38,10 +40,12 @@ import android.content.pm.Signature; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.provider.Settings; +import android.system.OsConstants; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; @@ -57,6 +61,7 @@ import com.android.server.backup.utils.FullBackupRestoreObserverUtils; import com.android.server.backup.utils.RestoreUtils; import com.android.server.backup.utils.TarBackupReader; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -135,6 +140,8 @@ public class FullRestoreEngine extends RestoreEngine { private boolean mPipesClosed; private final BackupEligibilityRules mBackupEligibilityRules; + private FileMetadata mReadOnlyParent = null; + public FullRestoreEngine( UserBackupManagerService backupManagerService, OperationStorage operationStorage, BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, @@ -158,6 +165,22 @@ public class FullRestoreEngine extends RestoreEngine { mBackupEligibilityRules = backupEligibilityRules; } + @VisibleForTesting + FullRestoreEngine() { + mIsAdbRestore = false; + mAllowApks = false; + mEphemeralOpToken = 0; + mUserId = 0; + mBackupEligibilityRules = null; + mAgentTimeoutParameters = null; + mBuffer = null; + mBackupManagerService = null; + mOperationStorage = null; + mMonitor = null; + mMonitorTask = null; + mOnlyPackage = null; + } + public IBackupAgent getAgent() { return mAgent; } @@ -397,6 +420,11 @@ public class FullRestoreEngine extends RestoreEngine { okay = false; } + if (shouldSkipReadOnlyDir(info)) { + // b/194894879: We don't support restore of read-only dirs. + okay = false; + } + // At this point we have an agent ready to handle the full // restore data as well as a pipe for sending data to // that agent. Tell the agent to start reading from the @@ -573,6 +601,45 @@ public class FullRestoreEngine extends RestoreEngine { return (info != null); } + boolean shouldSkipReadOnlyDir(FileMetadata info) { + if (isValidParent(mReadOnlyParent, info)) { + // This file has a read-only parent directory, we shouldn't + // restore it. + return true; + } else { + // We're now in a different branch of the file tree, update the parent + // value. + if (isReadOnlyDir(info)) { + // Current directory is read-only. Remember it so that we can skip all + // of its contents. + mReadOnlyParent = info; + Slog.w(TAG, "Skipping restore of " + info.path + " and its contents as " + + "read-only dirs are currently not supported."); + return true; + } else { + mReadOnlyParent = null; + } + } + + return false; + } + + private static boolean isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir) { + return parentDir != null + && childDir.packageName.equals(parentDir.packageName) + && childDir.domain.equals(parentDir.domain) + && childDir.path.startsWith(getPathWithTrailingSeparator(parentDir.path)); + } + + private static String getPathWithTrailingSeparator(String path) { + return path.endsWith(File.separator) ? path : path + File.separator; + } + + private static boolean isReadOnlyDir(FileMetadata file) { + // Check if owner has 'write' bit in the file's mode value (see 'man -7 inode' for details). + return file.type == BackupAgent.TYPE_DIRECTORY && (file.mode & OsConstants.S_IWUSR) == 0; + } + private void setUpPipes() throws IOException { synchronized (mPipesLock) { mPipes = ParcelFileDescriptor.createPipe(); diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index 7a5fa628f645..47d2640ffc3d 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -26,6 +26,7 @@ import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature; import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation; import static com.android.server.companion.RolesUtils.isRoleHolder; +import static com.android.server.companion.Utils.prepareForIpc; import static java.util.Objects.requireNonNull; @@ -47,7 +48,6 @@ import android.net.MacAddress; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; @@ -413,20 +413,4 @@ class AssociationRequestsProcessor { return requestingPackageSignatureAllowlisted; } - - /** - * Convert an instance of a "locally-defined" ResultReceiver to an instance of - * {@link android.os.ResultReceiver} itself, which the receiving process will be able to - * unmarshall. - */ - private static <T extends ResultReceiver> ResultReceiver prepareForIpc(T resultReceiver) { - final Parcel parcel = Parcel.obtain(); - resultReceiver.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - - final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel); - parcel.recycle(); - - return ipcFriendly; - } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index ac0944be9739..c362b8ed343d 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -99,6 +99,8 @@ import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.companion.datatransfer.SystemDataTransferProcessor; +import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; import com.android.server.companion.presence.CompanionDevicePresenceMonitor; import com.android.server.pm.UserManagerInternal; @@ -130,7 +132,9 @@ public class CompanionDeviceManagerService extends SystemService { private final PersistUserStateHandler mUserPersistenceHandler; private final AssociationStoreImpl mAssociationStore; + private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; private AssociationRequestsProcessor mAssociationRequestsProcessor; + private SystemDataTransferProcessor mSystemDataTransferProcessor; private CompanionDevicePresenceMonitor mDevicePresenceMonitor; private CompanionApplicationController mCompanionAppController; @@ -164,6 +168,7 @@ public class CompanionDeviceManagerService extends SystemService { mUserPersistenceHandler = new PersistUserStateHandler(); mAssociationStore = new AssociationStoreImpl(); + mSystemDataTransferRequestStore = new SystemDataTransferRequestStore(); } @Override @@ -178,6 +183,8 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationRequestsProcessor = new AssociationRequestsProcessor( /* cdmService */this, mAssociationStore); + mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore, + mSystemDataTransferRequestStore); final Context context = getContext(); mCompanionAppController = new CompanionApplicationController( @@ -602,6 +609,18 @@ public class CompanionDeviceManagerService extends SystemService { } @Override + public PendingIntent buildPermissionTransferUserConsentIntent(String packageName, + int userId, int associationId) throws RemoteException { + return mSystemDataTransferProcessor.buildPermissionTransferUserConsentIntent( + packageName, userId, associationId); + } + + @Override + public void startSystemDataTransfer(int userId, int associationId) throws RemoteException { + // TODO(b/222121838) + } + + @Override public void notifyDeviceAppeared(int associationId) { if (DEBUG) Log.i(TAG, "notifyDevice_Appeared() id=" + associationId); @@ -860,6 +879,9 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationStore.removeAssociation(associationId); logRemoveAssociation(deviceProfile); + // Remove all the system data transfer requests for the association. + mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId); + final List<AssociationInfo> otherAssociations = mAssociationStore.getAssociationsForPackage(userId, packageName); diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java index 8ac741a44ee5..73e68ec0bf97 100644 --- a/services/companion/java/com/android/server/companion/DataStoreUtils.java +++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java @@ -33,15 +33,24 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.FileOutputStream; -final class DataStoreUtils { +/** + * Util class for CDM data stores + */ +public final class DataStoreUtils { private static final String TAG = "CompanionDevice_DataStoreUtils"; - static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag) + /** + * Check if the parser pointer is at the start of the tag + */ + public static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag) throws XmlPullParserException { return parser.getEventType() == START_TAG && tag.equals(parser.getName()); } - static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag) + /** + * Check if the parser pointer is at the end of the tag + */ + public static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag) throws XmlPullParserException { return parser.getEventType() == END_TAG && tag.equals(parser.getName()); } @@ -57,7 +66,7 @@ final class DataStoreUtils { * @return an AtomicFile for the user */ @NonNull - static AtomicFile createStorageFileForUser(@UserIdInt int userId, String fileName) { + public static AtomicFile createStorageFileForUser(@UserIdInt int userId, String fileName) { return new AtomicFile(getBaseStorageFileForUser(userId, fileName)); } @@ -70,7 +79,7 @@ final class DataStoreUtils { * Writing to file could fail, for example, if the user has been recently removed and so was * their DE (/data/system_de/<user-id>/) directory. */ - static void writeToFileSafely( + public static void writeToFileSafely( @NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) { try { file.write(consumer); diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java index a2b20593a9cb..42c7687f0f28 100644 --- a/services/companion/java/com/android/server/companion/PackageUtils.java +++ b/services/companion/java/com/android/server/companion/PackageUtils.java @@ -41,8 +41,8 @@ import android.util.Slog; import com.android.internal.util.ArrayUtils; +import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -87,7 +87,8 @@ final class PackageUtils { final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser( COMPANION_SERVICE_INTENT, ResolveInfoFlags.of(0), userId); - final Map<String, List<ComponentName>> packageNameToServiceInfoList = new HashMap<>(); + final Map<String, List<ComponentName>> packageNameToServiceInfoList = + new HashMap<>(companionServices.size()); for (ResolveInfo resolveInfo : companionServices) { final ServiceInfo service = resolveInfo.serviceInfo; @@ -101,19 +102,19 @@ final class PackageUtils { continue; } - // Use LinkedList, because we'll need to prepend "primary" services, while appending the - // other (non-primary) services to the list. - final LinkedList<ComponentName> services = - (LinkedList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent( - service.packageName, it -> new LinkedList<>()); + // We'll need to prepend "primary" services, while appending the other (non-primary) + // services to the list. + final ArrayList<ComponentName> services = + (ArrayList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent( + service.packageName, it -> new ArrayList<>(1)); final ComponentName componentName = service.getComponentName(); if (isPrimaryCompanionDeviceService(pm, componentName)) { // "Primary" service should be at the head of the list. - services.addFirst(componentName); + services.add(0, componentName); } else { - services.addLast(componentName); + services.add(componentName); } } diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java index 36393894f727..4d42838fff50 100644 --- a/services/companion/java/com/android/server/companion/PersistentDataStore.java +++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java @@ -103,7 +103,7 @@ import java.util.concurrent.ConcurrentMap; * Since Android T the data is stored to "companion_device_manager.xml" file in * {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}. * - * See {@link #getBaseStorageFileForUser(int) getBaseStorageFileForUser()} + * See {@link #getStorageFileForUser(int)} * * <p> * Since Android T the data is stored using the v1 schema. diff --git a/services/companion/java/com/android/server/companion/Utils.java b/services/companion/java/com/android/server/companion/Utils.java new file mode 100644 index 000000000000..b9f61ecd8c4f --- /dev/null +++ b/services/companion/java/com/android/server/companion/Utils.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 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.companion; + +import android.os.Parcel; +import android.os.ResultReceiver; + +/** + * A miscellaneous util class for CDM + * + * @hide + */ +public final class Utils { + + /** + * Convert an instance of a "locally-defined" ResultReceiver to an instance of + * {@link android.os.ResultReceiver} itself, which the receiving process will be able to + * unmarshall. + * @hide + */ + public static <T extends ResultReceiver> ResultReceiver prepareForIpc(T resultReceiver) { + final Parcel parcel = Parcel.obtain(); + resultReceiver.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel); + parcel.recycle(); + + return ipcFriendly; + } + + private Utils() {} +} diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java new file mode 100644 index 000000000000..ca47453b26a4 --- /dev/null +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2022 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.companion.datatransfer; + +import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; +import static android.app.PendingIntent.FLAG_IMMUTABLE; +import static android.app.PendingIntent.FLAG_ONE_SHOT; +import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME; +import static android.content.ComponentName.createRelative; + +import static com.android.server.companion.Utils.prepareForIpc; + +import android.annotation.UserIdInt; +import android.app.PendingIntent; +import android.companion.AssociationInfo; +import android.companion.DeviceNotAssociatedException; +import android.companion.datatransfer.PermissionSyncRequest; +import android.companion.datatransfer.SystemDataTransferRequest; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.util.Slog; + +import com.android.server.companion.AssociationStore; +import com.android.server.companion.CompanionDeviceManagerService; + +import java.util.List; + +/** + * This processor builds user consent intent for a given SystemDataTransferRequest and processes the + * request when the system is ready (a secure channel is established between the handhold and the + * companion device). + */ +public class SystemDataTransferProcessor { + + private static final String LOG_TAG = SystemDataTransferProcessor.class.getSimpleName(); + + // Values from UI to SystemDataTransferProcessor via ResultReceiver + private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0; + private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1; + private static final String EXTRA_PERMISSION_SYNC_REQUEST = "permission_sync_request"; + private static final String EXTRA_COMPANION_DEVICE_NAME = "companion_device_name"; + private static final String EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER = + "system_data_transfer_result_receiver"; + private static final ComponentName SYSTEM_DATA_TRANSFER_REQUEST_APPROVAL_ACTIVITY = + createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, + ".CompanionDeviceDataTransferActivity"); + + private final Context mContext; + private final AssociationStore mAssociationStore; + private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; + + public SystemDataTransferProcessor(CompanionDeviceManagerService service, + AssociationStore associationStore, + SystemDataTransferRequestStore systemDataTransferRequestStore) { + mContext = service.getContext(); + mAssociationStore = associationStore; + mSystemDataTransferRequestStore = systemDataTransferRequestStore; + } + + /** + * Build a PendingIntent of permission sync user consent dialog + */ + public PendingIntent buildPermissionTransferUserConsentIntent(String packageName, + @UserIdInt int userId, int associationId) throws RemoteException { + // The association must exist and either belong to the calling package, + // or the calling package must hold REQUEST_SYSTEM_DATA_TRANSFER permission. + AssociationInfo association = mAssociationStore.getAssociationById(associationId); + if (association == null) { + throw new RemoteException(new DeviceNotAssociatedException( + "Association id: " + associationId + " doesn't exist.")); + } else { + if (!association.getPackageName().equals(packageName)) { + Slog.e(LOG_TAG, "The calling package doesn't own the association."); + return null; + } + + // Check if the request's data type has been requested before. + List<SystemDataTransferRequest> storedRequests = + mSystemDataTransferRequestStore.readRequestsByAssociationId(userId, + associationId); + for (SystemDataTransferRequest storedRequest : storedRequests) { + if (storedRequest instanceof PermissionSyncRequest) { + Slog.e(LOG_TAG, "The request has been sent before, you can not send " + + "the same request type again."); + return null; + } + } + } + + Slog.i(LOG_TAG, "Creating permission sync intent for userId=" + userId + + "associationId: " + associationId); + + // Create an internal intent to launch the user consent dialog + final Bundle extras = new Bundle(); + PermissionSyncRequest request = new PermissionSyncRequest(associationId); + request.setUserId(userId); + extras.putParcelable(EXTRA_PERMISSION_SYNC_REQUEST, request); + extras.putCharSequence(EXTRA_COMPANION_DEVICE_NAME, association.getDisplayName()); + extras.putParcelable(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER, + prepareForIpc(mOnSystemDataTransferRequestConfirmationReceiver)); + + final Intent intent = new Intent(); + intent.setComponent(SYSTEM_DATA_TRANSFER_REQUEST_APPROVAL_ACTIVITY); + intent.putExtras(extras); + + // Create a PendingIntent + final long token = Binder.clearCallingIdentity(); + try { + return PendingIntent.getActivity(mContext, /*requestCode */ associationId, intent, + FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private final ResultReceiver mOnSystemDataTransferRequestConfirmationReceiver = + new ResultReceiver(Handler.getMain()) { + @Override + protected void onReceiveResult(int resultCode, Bundle data) { + Slog.d(LOG_TAG, "onReceiveResult() code=" + resultCode + ", " + + "data=" + data); + + if (resultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED + || resultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED) { + final PermissionSyncRequest request = + data.getParcelable(EXTRA_PERMISSION_SYNC_REQUEST, + PermissionSyncRequest.class); + if (request != null) { + request.setUserConsented( + resultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED); + Slog.i(LOG_TAG, "Recording request: " + request); + mSystemDataTransferRequestStore.writeRequest(request.getUserId(), + request); + } + + return; + } + + Slog.e(LOG_TAG, "Unknown result code:" + resultCode); + } + }; +} diff --git a/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java index 38e5d16842b9..ab0a06260cfc 100644 --- a/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.companion; +package com.android.server.companion.datatransfer; + +import static android.companion.datatransfer.SystemDataTransferRequest.DATA_TYPE_PERMISSION_SYNC; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; -import static com.android.internal.util.XmlUtils.readThisListXml; import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; -import static com.android.internal.util.XmlUtils.writeListXml; import static com.android.server.companion.DataStoreUtils.createStorageFileForUser; import static com.android.server.companion.DataStoreUtils.isEndOfTag; import static com.android.server.companion.DataStoreUtils.isStartOfTag; @@ -30,13 +30,16 @@ import static com.android.server.companion.DataStoreUtils.writeToFileSafely; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.companion.SystemDataTransferRequest; +import android.companion.datatransfer.PermissionSyncRequest; +import android.companion.datatransfer.SystemDataTransferRequest; import android.util.AtomicFile; import android.util.Slog; +import android.util.SparseArray; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParserException; @@ -45,46 +48,130 @@ import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * The class is responsible for reading/writing SystemDataTransferRequest records from/to the disk. - * + * <p> * The following snippet is a sample XML file stored in the disk. * <pre>{@code * <requests> * <request * association_id="1" - * is_permission_sync_all_packages="false"> - * <list name="permission_sync_packages"> - * <string>com.sample.app1</string> - * <string>com.sample.app2</string> - * </list> + * data_type="1" + * user_id="12" + * is_user_consented="true" * </request> * </requests> * }</pre> */ -public class SystemDataTransferRequestDataStore { +public class SystemDataTransferRequestStore { - private static final String LOG_TAG = SystemDataTransferRequestDataStore.class.getSimpleName(); + private static final String LOG_TAG = SystemDataTransferRequestStore.class.getSimpleName(); private static final String FILE_NAME = "companion_device_system_data_transfer_requests.xml"; private static final String XML_TAG_REQUESTS = "requests"; private static final String XML_TAG_REQUEST = "request"; - private static final String XML_TAG_LIST = "list"; private static final String XML_ATTR_ASSOCIATION_ID = "association_id"; - private static final String XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES = - "is_permission_sync_all_packages"; - private static final String XML_ATTR_PERMISSION_SYNC_PACKAGES = "permission_sync_packages"; + private static final String XML_ATTR_DATA_TYPE = "data_type"; + private static final String XML_ATTR_USER_ID = "user_id"; + private static final String XML_ATTR_IS_USER_CONSENTED = "is_user_consented"; + + private static final int READ_FROM_DISK_TIMEOUT = 5; // in seconds + private final ExecutorService mExecutor; private final ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile = new ConcurrentHashMap<>(); + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final SparseArray<ArrayList<SystemDataTransferRequest>> mCachedPerUser = + new SparseArray<>(); + + public SystemDataTransferRequestStore() { + mExecutor = Executors.newSingleThreadExecutor(); + } + + @NonNull + List<SystemDataTransferRequest> readRequestsByAssociationId(@UserIdInt int userId, + int associationId) { + List<SystemDataTransferRequest> cachedRequests; + synchronized (mLock) { + cachedRequests = readRequestsFromCache(userId); + } + + List<SystemDataTransferRequest> requestsByAssociationId = new ArrayList<>(); + for (SystemDataTransferRequest request : cachedRequests) { + if (request.getAssociationId() == associationId) { + requestsByAssociationId.add(request); + } + } + return requestsByAssociationId; + } + + void writeRequest(@UserIdInt int userId, SystemDataTransferRequest request) { + Slog.i(LOG_TAG, "Writing request=" + request + " to store."); + ArrayList<SystemDataTransferRequest> cachedRequests; + synchronized (mLock) { + // Write to cache + cachedRequests = readRequestsFromCache(userId); + cachedRequests.add(request); + mCachedPerUser.set(userId, cachedRequests); + } + // Write to store + mExecutor.execute(() -> writeRequestsToStore(userId, cachedRequests)); + } + + /** + * Remove requests by association id. userId must be the one which owns the associationId. + */ + public void removeRequestsByAssociationId(@UserIdInt int userId, int associationId) { + Slog.i(LOG_TAG, "Removing system data transfer requests for userId=" + userId + + ", associationId=" + associationId); + ArrayList<SystemDataTransferRequest> cachedRequests; + synchronized (mLock) { + // Remove requests from cache + cachedRequests = readRequestsFromCache(userId); + cachedRequests.removeIf(request -> request.getAssociationId() == associationId); + mCachedPerUser.set(userId, cachedRequests); + } + // Remove requests from store + mExecutor.execute(() -> writeRequestsToStore(userId, cachedRequests)); + } + + @GuardedBy("mLock") + private ArrayList<SystemDataTransferRequest> readRequestsFromCache(@UserIdInt int userId) { + ArrayList<SystemDataTransferRequest> cachedRequests = mCachedPerUser.get(userId); + if (cachedRequests == null) { + Future<ArrayList<SystemDataTransferRequest>> future = + mExecutor.submit(() -> readRequestsFromStore(userId)); + try { + cachedRequests = future.get(READ_FROM_DISK_TIMEOUT, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Slog.e(LOG_TAG, "Thread reading SystemDataTransferRequest from disk is " + + "interrupted."); + } catch (ExecutionException e) { + Slog.e(LOG_TAG, "Error occurred while reading SystemDataTransferRequest " + + "from disk."); + } catch (TimeoutException e) { + Slog.e(LOG_TAG, "Reading SystemDataTransferRequest from disk timed out."); + } + mCachedPerUser.set(userId, cachedRequests); + } + return cachedRequests; + } + /** * Reads previously persisted data for the given user * @@ -92,7 +179,7 @@ public class SystemDataTransferRequestDataStore { * @return a list of SystemDataTransferRequest */ @NonNull - List<SystemDataTransferRequest> readRequestsForUser(@UserIdInt int userId) { + private ArrayList<SystemDataTransferRequest> readRequestsFromStore(@UserIdInt int userId) { final AtomicFile file = getStorageFileForUser(userId); Slog.i(LOG_TAG, "Reading SystemDataTransferRequests for user " + userId + " from " + "file=" + file.getBaseFile().getPath()); @@ -102,59 +189,62 @@ public class SystemDataTransferRequestDataStore { synchronized (file) { if (!file.getBaseFile().exists()) { Slog.d(LOG_TAG, "File does not exist -> Abort"); - return Collections.emptyList(); + return new ArrayList<>(); } try (FileInputStream in = file.openRead()) { final TypedXmlPullParser parser = Xml.resolvePullParser(in); XmlUtils.beginDocument(parser, XML_TAG_REQUESTS); - return readRequests(parser); + return readRequestsFromXml(parser); } catch (XmlPullParserException | IOException e) { Slog.e(LOG_TAG, "Error while reading requests file", e); - return Collections.emptyList(); + return new ArrayList<>(); } } } @NonNull - private List<SystemDataTransferRequest> readRequests(@NonNull TypedXmlPullParser parser) - throws XmlPullParserException, IOException { + private ArrayList<SystemDataTransferRequest> readRequestsFromXml( + @NonNull TypedXmlPullParser parser) throws XmlPullParserException, IOException { if (!isStartOfTag(parser, XML_TAG_REQUESTS)) { throw new XmlPullParserException("The XML doesn't have start tag: " + XML_TAG_REQUESTS); } - List<SystemDataTransferRequest> requests = new ArrayList<>(); + ArrayList<SystemDataTransferRequest> requests = new ArrayList<>(); while (true) { parser.nextTag(); - if (isEndOfTag(parser, XML_TAG_REQUESTS)) break; + if (isEndOfTag(parser, XML_TAG_REQUESTS)) { + break; + } if (isStartOfTag(parser, XML_TAG_REQUEST)) { - requests.add(readRequest(parser)); + requests.add(readRequestFromXml(parser)); } } return requests; } - private SystemDataTransferRequest readRequest(@NonNull TypedXmlPullParser parser) + private SystemDataTransferRequest readRequestFromXml(@NonNull TypedXmlPullParser parser) throws XmlPullParserException, IOException { if (!isStartOfTag(parser, XML_TAG_REQUEST)) { throw new XmlPullParserException("XML doesn't have start tag: " + XML_TAG_REQUEST); } final int associationId = readIntAttribute(parser, XML_ATTR_ASSOCIATION_ID); - final boolean isPermissionSyncAllPackages = readBooleanAttribute(parser, - XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES); - parser.nextTag(); - List<String> permissionSyncPackages = new ArrayList<>(); - if (isStartOfTag(parser, XML_TAG_LIST)) { - parser.nextTag(); - permissionSyncPackages = readThisListXml(parser, XML_TAG_LIST, - new String[1]); - } + final int dataType = readIntAttribute(parser, XML_ATTR_DATA_TYPE); + final int userId = readIntAttribute(parser, XML_ATTR_USER_ID); + final boolean isUserConsented = readBooleanAttribute(parser, XML_ATTR_IS_USER_CONSENTED); - return new SystemDataTransferRequest(associationId, isPermissionSyncAllPackages, - permissionSyncPackages); + switch (dataType) { + case DATA_TYPE_PERMISSION_SYNC: + PermissionSyncRequest request = new PermissionSyncRequest(associationId); + request.setUserId(userId); + request.setUserConsented(isUserConsented); + return request; + default: + return null; + } } /** @@ -163,7 +253,7 @@ public class SystemDataTransferRequestDataStore { * @param userId Android UserID * @param requests a list of user's SystemDataTransferRequest. */ - void writeRequestsForUser(@UserIdInt int userId, + void writeRequestsToStore(@UserIdInt int userId, @NonNull List<SystemDataTransferRequest> requests) { final AtomicFile file = getStorageFileForUser(userId); Slog.i(LOG_TAG, "Writing SystemDataTransferRequests for user " + userId + " to file=" @@ -177,37 +267,31 @@ public class SystemDataTransferRequestDataStore { serializer.setFeature( "http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); - writeRequests(serializer, requests); + writeRequestsToXml(serializer, requests); serializer.endDocument(); }); } } - private void writeRequests(@NonNull TypedXmlSerializer serializer, + private void writeRequestsToXml(@NonNull TypedXmlSerializer serializer, @Nullable Collection<SystemDataTransferRequest> requests) throws IOException { serializer.startTag(null, XML_TAG_REQUESTS); for (SystemDataTransferRequest request : requests) { - writeRequest(serializer, request); + writeRequestToXml(serializer, request); } serializer.endTag(null, XML_TAG_REQUESTS); } - private void writeRequest(@NonNull TypedXmlSerializer serializer, + private void writeRequestToXml(@NonNull TypedXmlSerializer serializer, @NonNull SystemDataTransferRequest request) throws IOException { serializer.startTag(null, XML_TAG_REQUEST); writeIntAttribute(serializer, XML_ATTR_ASSOCIATION_ID, request.getAssociationId()); - writeBooleanAttribute(serializer, XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES, - request.isPermissionSyncAllPackages()); - try { - writeListXml(request.getPermissionSyncPackages(), XML_ATTR_PERMISSION_SYNC_PACKAGES, - serializer); - } catch (XmlPullParserException e) { - Slog.e(LOG_TAG, "Error writing permission sync packages into XML. " - + request.getPermissionSyncPackages().toString()); - } + writeIntAttribute(serializer, XML_ATTR_DATA_TYPE, request.getDataType()); + writeIntAttribute(serializer, XML_ATTR_USER_ID, request.getUserId()); + writeBooleanAttribute(serializer, XML_ATTR_IS_USER_CONSENTED, request.isUserConsented()); serializer.endTag(null, XML_TAG_REQUEST); } @@ -215,11 +299,12 @@ public class SystemDataTransferRequestDataStore { /** * Creates and caches {@link AtomicFile} object that represents the back-up file for the given * user. - * + * <p> * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it * possible to synchronize reads and writes to the file using the returned object. */ - private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) { + @NonNull + private AtomicFile getStorageFileForUser(@UserIdInt int userId) { return mUserIdToStorageFile.computeIfAbsent(userId, u -> createStorageFileForUser(userId, FILE_NAME)); } diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 06f698efde2b..58c22a75cd5b 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -552,18 +552,23 @@ public abstract class PackageManagerInternal { /** * Set which overlay to use for a package. * @param userId The user for which to update the overlays. - * @param targetPackageName The package name of the package for which to update the overlays. - * @param overlayPaths The complete list of overlay paths that should be enabled for + * @param pendingChanges is a map to describe all overlay targets and their related overlay + * paths. Its key is the overlay target package and its value is the + * complete list of overlay paths that should be enabled for * the target. Previously enabled overlays not specified in the list * will be disabled. Pass in null or empty paths to disable all overlays. * The order of the items is significant if several overlays modify the - * same resource. + * same resource. To pass the concrete ArrayMap type is to reduce the + * overheads of system server. * @param outUpdatedPackageNames An output list that contains the package names of packages * affected by the update of enabled overlays. - * @return true if all packages names were known by the package manager, false otherwise + * @param outInvalidPackageNames An output list that contains the package names of packages + * are not valid. */ - public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName, - @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames); + public abstract void setEnabledOverlayPackages(int userId, + @NonNull ArrayMap<String, OverlayPaths> pendingChanges, + @NonNull Set<String> outUpdatedPackageNames, + @NonNull Set<String> outInvalidPackageNames); /** * Resolves an activity intent, allowing instant apps to be resolved. diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java index c4e84a4cd138..a9bdf063e8a6 100644 --- a/services/core/java/com/android/server/ConsumerIrService.java +++ b/services/core/java/com/android/server/ConsumerIrService.java @@ -16,6 +16,10 @@ package com.android.server; +import static android.Manifest.permission.TRANSMIT_IR; + +import android.annotation.EnforcePermission; +import android.annotation.RequiresNoPermission; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.IConsumerIrService; @@ -60,6 +64,7 @@ public class ConsumerIrService extends IConsumerIrService.Stub { } @Override + @RequiresNoPermission public boolean hasIrEmitter() { return mHasNativeHal; } @@ -85,12 +90,8 @@ public class ConsumerIrService extends IConsumerIrService.Stub { @Override + @EnforcePermission(TRANSMIT_IR) public void transmit(String packageName, int carrierFrequency, int[] pattern) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TRANSMIT_IR) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires TRANSMIT_IR permission"); - } - long totalXmitTime = 0; for (int slice : pattern) { @@ -125,12 +126,8 @@ public class ConsumerIrService extends IConsumerIrService.Stub { } @Override + @EnforcePermission(TRANSMIT_IR) public int[] getCarrierFrequencies() { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TRANSMIT_IR) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires TRANSMIT_IR permission"); - } - throwIfNoIrEmitter(); synchronized(mHalLock) { diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 78df983c83f7..a562afbe442f 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -23,6 +23,7 @@ import android.app.ActivityManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Environment; import android.os.SystemClock; @@ -162,16 +163,17 @@ public final class SystemServiceManager implements Dumpable { /** * Returns true if the jar is in a test APEX. */ - private static boolean isJarInTestApex(String pathStr) { + private boolean isJarInTestApex(String pathStr) { Path path = Paths.get(pathStr); if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) { String apexModuleName = path.getName(1).toString(); ApexManager apexManager = ApexManager.getInstance(); String packageName = apexManager.getActivePackageNameForApexModuleName(apexModuleName); - PackageInfo packageInfo = apexManager.getPackageInfo( - packageName, ApexManager.MATCH_ACTIVE_PACKAGE); - if (packageInfo != null) { + try { + PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(packageName, + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX)); return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0; + } catch (Exception ignore) { } } return false; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index b059cc7e2aa2..2c465f44aa99 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1820,6 +1820,14 @@ public class AccountManagerService if (account == null) { return false; } + if (account.name != null && account.name.length() > 200) { + Log.w(TAG, "Account cannot be added - Name longer than 200 chars"); + return false; + } + if (account.type != null && account.type.length() > 200) { + Log.w(TAG, "Account cannot be added - Name longer than 200 chars"); + return false; + } if (!isLocalUnlockedUser(accounts.userId)) { Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user " + accounts.userId + " is locked. callingUid=" + callingUid); @@ -2065,6 +2073,10 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (accountToRename == null) throw new IllegalArgumentException("account is null"); + if (newName != null && newName.length() > 200) { + Log.e(TAG, "renameAccount failed - account name longer than 200"); + throw new IllegalArgumentException("account name longer than 200"); + } int userId = UserHandle.getCallingUserId(); if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) { String msg = String.format( diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index 297d28dadde3..ad89afb791cc 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -892,15 +892,6 @@ public class AdbDebuggingManager { case MESSAGE_ADB_CONFIRM: { String key = (String) msg.obj; - if ("trigger_restart_min_framework".equals( - SystemProperties.get("vold.decrypt"))) { - Slog.w(TAG, "Deferring adb confirmation until after vold decrypt"); - if (mThread != null) { - mThread.sendResponse("NO"); - logAdbConnectionChanged(key, AdbProtoEnums.DENIED_VOLD_DECRYPT, false); - } - break; - } String fingerprints = getFingerprints(key); if ("".equals(fingerprints)) { if (mThread != null) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index aa2f7c13bc77..e4e5b8c64d06 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -418,6 +418,7 @@ import com.android.server.uri.GrantUri; import com.android.server.uri.NeededUriGrants; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.PriorityDump; +import com.android.server.utils.Slogf; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityMetricsLaunchObserver; @@ -8731,8 +8732,8 @@ public class ActivityManagerService extends IActivityManager.Stub sb.append("Foreground: ") .append(process.isInterestingToUserLocked() ? "Yes" : "No") .append("\n"); - if (process.getStartTime() > 0) { - long runtimeMillis = SystemClock.elapsedRealtime() - process.getStartTime(); + if (process.getStartUptime() > 0) { + long runtimeMillis = SystemClock.uptimeMillis() - process.getStartUptime(); sb.append("Process-Runtime: ").append(runtimeMillis).append("\n"); } } @@ -9470,7 +9471,7 @@ public class ActivityManagerService extends IActivityManager.Stub opti++; } synchronized (this) { - dumpBroadcastsLocked(fd, pw, args, opti, true, dumpPackage); + dumpBroadcastsLocked(fd, pw, args, opti, /* dumpAll= */ true, dumpPackage); } } else if ("broadcast-stats".equals(cmd)) { if (opti < args.length) { @@ -10409,6 +10410,8 @@ public class ActivityManagerService extends IActivityManager.Stub boolean needSep = false; boolean onlyHistory = false; boolean printedAnything = false; + boolean onlyReceivers = false; + int filteredUid = Process.INVALID_UID; if ("history".equals(dumpPackage)) { if (opti < args.length && "-s".equals(args[opti])) { @@ -10417,6 +10420,31 @@ public class ActivityManagerService extends IActivityManager.Stub onlyHistory = true; dumpPackage = null; } + if ("receivers".equals(dumpPackage)) { + onlyReceivers = true; + dumpPackage = null; + if (opti + 2 <= args.length) { + for (int i = opti; i < args.length; i++) { + String arg = args[i]; + switch (arg) { + case "--uid": + filteredUid = getIntArg(pw, args, ++i, Process.INVALID_UID); + if (filteredUid == Process.INVALID_UID) { + return; + } + break; + default: + pw.printf("Invalid argument at index %d: %s\n", i, arg); + return; + } + } + } + } + if (DEBUG_BROADCAST) { + Slogf.d(TAG_BROADCAST, "dumpBroadcastsLocked(): dumpPackage=%s, onlyHistory=%b, " + + "onlyReceivers=%b, filteredUid=%d", dumpPackage, onlyHistory, onlyReceivers, + filteredUid); + } pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)"); if (!onlyHistory && dumpAll) { @@ -10429,6 +10457,13 @@ public class ActivityManagerService extends IActivityManager.Stub !dumpPackage.equals(r.app.info.packageName))) { continue; } + if (filteredUid != Process.INVALID_UID && filteredUid != r.app.uid) { + if (DEBUG_BROADCAST) { + Slogf.v(TAG_BROADCAST, "dumpBroadcastsLocked(): skipping receiver whose" + + " uid (%d) is not %d: %s", r.app.uid, filteredUid, r.app); + } + continue; + } if (!printed) { pw.println(" Registered Receivers:"); needSep = true; @@ -10438,24 +10473,32 @@ public class ActivityManagerService extends IActivityManager.Stub pw.print(" * "); pw.println(r); r.dump(pw, " "); } + } else { + if (onlyReceivers) { + pw.println(" (no registered receivers)"); + } } - if (mReceiverResolver.dump(pw, needSep ? - "\n Receiver Resolver Table:" : " Receiver Resolver Table:", - " ", dumpPackage, false, false)) { - needSep = true; - printedAnything = true; + if (!onlyReceivers) { + if (mReceiverResolver.dump(pw, needSep + ? "\n Receiver Resolver Table:" : " Receiver Resolver Table:", + " ", dumpPackage, false, false)) { + needSep = true; + printedAnything = true; + } } } - for (BroadcastQueue q : mBroadcastQueues) { - needSep = q.dumpLocked(fd, pw, args, opti, dumpAll, dumpPackage, needSep); - printedAnything |= needSep; + if (!onlyReceivers) { + for (BroadcastQueue q : mBroadcastQueues) { + needSep = q.dumpLocked(fd, pw, args, opti, dumpAll, dumpPackage, needSep); + printedAnything |= needSep; + } } needSep = true; - if (!onlyHistory && mStickyBroadcasts != null && dumpPackage == null) { + if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null && dumpPackage == null) { for (int user=0; user<mStickyBroadcasts.size(); user++) { if (needSep) { pw.println(); @@ -10490,7 +10533,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - if (!onlyHistory && dumpAll) { + if (!onlyHistory && !onlyReceivers && dumpAll) { pw.println(); for (BroadcastQueue queue : mBroadcastQueues) { pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]=" @@ -18381,4 +18424,24 @@ public class ActivityManagerService extends IActivityManager.Stub Trace.traceBegin(traceTag, methodName + subInfo); } } + + /** + * Gets an {@code int} argument from the given {@code index} on {@code args}, logging an error + * message on {@code pw} when it cannot be parsed. + * + * Returns {@code int} argument or {@code invalidValue} if it could not be parsed. + */ + private static int getIntArg(PrintWriter pw, String[] args, int index, int invalidValue) { + if (index > args.length) { + pw.println("Missing argument"); + return invalidValue; + } + String arg = args[index]; + try { + return Integer.parseInt(arg); + } catch (Exception e) { + pw.printf("Non-numeric argument at index %d: %s\n", index, arg); + return invalidValue; + } + } } diff --git a/services/core/java/com/android/server/am/ReceiverList.java b/services/core/java/com/android/server/am/ReceiverList.java index ff34fd36de3e..4400de2f28b4 100644 --- a/services/core/java/com/android/server/am/ReceiverList.java +++ b/services/core/java/com/android/server/am/ReceiverList.java @@ -101,8 +101,12 @@ final class ReceiverList extends ArrayList<BroadcastFilter> void dumpLocal(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("app="); pw.print(app != null ? app.toShortString() : null); - pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.print(uid); - pw.print(" user="); pw.println(userId); + pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.print(uid); + pw.print(" user="); pw.print(userId); + if (app != null) { + pw.print(" #receivers="); pw.print(app.mReceivers.numberOfReceivers()); + } + pw.println(); if (curBroadcast != null || linkedToDeath) { pw.print(prefix); pw.print("curBroadcast="); pw.print(curBroadcast); pw.print(" linkedToDeath="); pw.println(linkedToDeath); diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 361629b0a629..f35d7929a8f7 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -2783,7 +2783,9 @@ public class AppOpsService extends IAppOpsService.Stub { @Nullable IAppOpsCallback permissionPolicyCallback) { enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid); verifyIncomingOp(code); - verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return; + } ArraySet<ModeCallback> repCbs = null; code = AppOpsManager.opToSwitch(code); @@ -3218,7 +3220,9 @@ public class AppOpsService extends IAppOpsService.Stub { private int checkOperationImpl(int code, int uid, String packageName, @Nullable String attributionTag, boolean raw) { verifyIncomingOp(code); - verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return AppOpsManager.opToDefaultMode(code); + } String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -3330,8 +3334,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private boolean isPackageExisted(String packageName) { - return LocalServices.getService(PackageManagerInternal.class) - .getPackageStateInternal(packageName) != null; + return getPackageManagerInternal().getPackageStateInternal(packageName) != null; } /** @@ -3366,8 +3369,11 @@ public class AppOpsService extends IAppOpsService.Stub { verifyIncomingProxyUid(attributionSource); verifyIncomingOp(code); - verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid)); - verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid)); + if (!isIncomingPackageValid(proxiedPackageName, UserHandle.getUserId(proxiedUid)) + || !isIncomingPackageValid(proxyPackageName, UserHandle.getUserId(proxyUid))) { + return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, proxiedAttributionTag, + proxiedPackageName); + } skipProxyOperation = skipProxyOperation && isCallerAndAttributionTrusted(attributionSource); @@ -3424,7 +3430,10 @@ public class AppOpsService extends IAppOpsService.Stub { @Nullable String message, boolean shouldCollectMessage) { verifyIncomingUid(uid); verifyIncomingOp(code); - verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, + packageName); + } String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -3830,7 +3839,10 @@ public class AppOpsService extends IAppOpsService.Stub { int attributionChainId) { verifyIncomingUid(uid); verifyIncomingOp(code); - verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, + packageName); + } String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -3884,8 +3896,11 @@ public class AppOpsService extends IAppOpsService.Stub { verifyIncomingProxyUid(attributionSource); verifyIncomingOp(code); - verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid)); - verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid)); + if (!isIncomingPackageValid(proxyPackageName, UserHandle.getUserId(proxyUid)) + || !isIncomingPackageValid(proxiedPackageName, UserHandle.getUserId(proxiedUid))) { + return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, proxiedAttributionTag, + proxiedPackageName); + } boolean isCallerTrusted = isCallerAndAttributionTrusted(attributionSource); skipProxyOperation = isCallerTrusted && skipProxyOperation; @@ -4070,7 +4085,9 @@ public class AppOpsService extends IAppOpsService.Stub { String attributionTag) { verifyIncomingUid(uid); verifyIncomingOp(code); - verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return; + } String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -4103,8 +4120,10 @@ public class AppOpsService extends IAppOpsService.Stub { verifyIncomingProxyUid(attributionSource); verifyIncomingOp(code); - verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid)); - verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid)); + if (!isIncomingPackageValid(proxyPackageName, UserHandle.getUserId(proxyUid)) + || !isIncomingPackageValid(proxiedPackageName, UserHandle.getUserId(proxiedUid))) { + return null; + } String resolvedProxyPackageName = AppOpsManager.resolvePackageName(proxyUid, proxyPackageName); @@ -4377,12 +4396,32 @@ public class AppOpsService extends IAppOpsService.Stub { throw new IllegalArgumentException("Bad operation #" + op); } - private void verifyIncomingPackage(@Nullable String packageName, @UserIdInt int userId) { - if (packageName != null && getPackageManagerInternal().filterAppAccess(packageName, - Binder.getCallingUid(), userId)) { - throw new IllegalArgumentException( - packageName + " not found from " + Binder.getCallingUid()); + private boolean isIncomingPackageValid(@Nullable String packageName, @UserIdInt int userId) { + final int callingUid = Binder.getCallingUid(); + // Handle the special UIDs that don't have actual packages (audioserver, cameraserver, etc). + if (packageName == null || isSpecialPackage(callingUid, packageName)) { + return true; } + + // If the package doesn't exist, #verifyAndGetBypass would throw a SecurityException in + // the end. Although that exception would be caught and return, we could make it return + // early. + if (!isPackageExisted(packageName)) { + return false; + } + + if (getPackageManagerInternal().filterAppAccess(packageName, callingUid, userId)) { + Slog.w(TAG, packageName + " not found from " + callingUid); + return false; + } + + return true; + } + + private boolean isSpecialPackage(int callingUid, @Nullable String packageName) { + final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid, packageName); + return callingUid == Process.SYSTEM_UID + || resolveUid(resolvedPackage) != Process.INVALID_UID; } private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) { @@ -6672,7 +6711,9 @@ public class AppOpsService extends IAppOpsService.Stub { } } verifyIncomingOp(code); - verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return false; + } final String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -7077,7 +7118,7 @@ public class AppOpsService extends IAppOpsService.Stub { private static int resolveUid(String packageName) { if (packageName == null) { - return -1; + return Process.INVALID_UID; } switch (packageName) { case "root": @@ -7092,7 +7133,7 @@ public class AppOpsService extends IAppOpsService.Stub { case "cameraserver": return Process.CAMERASERVER_UID; } - return -1; + return Process.INVALID_UID; } private static String[] getPackagesForUid(int uid) { diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 0e2582c23b86..e1ef277a2b8a 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -53,6 +53,7 @@ import android.hardware.biometrics.SensorProperties; import android.hardware.biometrics.SensorPropertiesInternal; import android.os.Binder; import android.os.Build; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -87,6 +88,13 @@ public class Utils { return true; } + /** If virtualized biometrics are supported (requires debug build). */ + public static boolean isVirtualEnabled(Context context) { + return Build.isDebuggable() + && Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + } + /** * Combines {@link PromptInfo#setDeviceCredentialAllowed(boolean)} with * {@link PromptInfo#setAuthenticators(int)}, as the former is not flexible enough. @@ -374,6 +382,15 @@ public class Utils { return false; } + /** Same as checkPermission but also allows shell. */ + public static void checkPermissionOrShell(Context context, String permission) { + if (Binder.getCallingUid() == Process.SHELL_UID) { + return; + } + checkPermission(context, permission); + } + + public static void checkPermission(Context context, String permission) { context.enforceCallingOrSelfPermission(permission, "Must have " + permission + " permission."); diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index 57ea812dbb3a..ded9c8de4dba 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -19,9 +19,11 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; +import android.os.Build; import android.os.IBinder; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.BiometricsProto; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; @@ -65,6 +67,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide private final List<S> mEnrolledList; private final boolean mHasEnrollmentsBeforeStarting; private BaseClientMonitor mCurrentTask; + private boolean mFavorHalEnrollments = false; private final ClientMonitorCallback mEnumerateCallback = new ClientMonitorCallback() { @Override @@ -87,7 +90,21 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide // InternalEnumerateClient. Finish this client. mCallback.onClientFinished(InternalCleanupClient.this, success); } else { - startCleanupUnknownHalTemplates(); + if (mFavorHalEnrollments && Build.isDebuggable()) { + // on debug builds, optionally allow the HAL be the source of + // truth for enrollments + try { + for (UserTemplate template : mUnknownHALTemplates) { + Slog.i(TAG, "Adding unknown HAL template: " + + template.mIdentifier.getBiometricId()); + onAddUnknownTemplate(template.mUserId, template.mIdentifier); + } + } finally { + mCallback.onClientFinished(InternalCleanupClient.this, success); + } + } else { + startCleanupUnknownHalTemplates(); + } } } }; @@ -197,8 +214,27 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide ((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining); } + /** When set unknown templates in the HAL will be added instead of deleted. */ + public void setFavorHalEnrollments() { + mFavorHalEnrollments = true; + } + + /** Called when an unknown template is found and setFavorHalEnrollments was requested. */ + protected void onAddUnknownTemplate(int userId, + @NonNull BiometricAuthenticator.Identifier identifier) {} + @Override public int getProtoEnum() { return BiometricsProto.CM_INTERNAL_CLEANUP; } + + @VisibleForTesting + public InternalEnumerateClient<T> getCurrentEnumerateClient() { + return (InternalEnumerateClient<T>) mCurrentTask; + } + + @VisibleForTesting + public RemovalClient<S, T> getCurrentRemoveClient() { + return (RemovalClient<S, T>) mCurrentTask; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index e0d519469e32..45ffa23dc66a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -50,8 +50,6 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, logger, biometricContext); - //, BiometricsProtoEnums.ACTION_REMOVE, - // BiometricsProtoEnums.CLIENT_UNKNOWN); mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty(); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java index 54f2033b363a..b0b23faa9aa5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java @@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.face.IFace; import android.hardware.face.Face; import android.os.IBinder; @@ -28,6 +29,7 @@ import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalCleanupClient; import com.android.server.biometrics.sensors.InternalEnumerateClient; import com.android.server.biometrics.sensors.RemovalClient; +import com.android.server.biometrics.sensors.face.FaceUtils; import java.util.List; import java.util.Map; @@ -68,4 +70,11 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession> null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner, utils, sensorId, logger, biometricContext, authenticatorIds); } + + @Override + protected void onAddUnknownTemplate(int userId, + @NonNull BiometricAuthenticator.Identifier identifier) { + FaceUtils.getInstance(getSensorId()).addBiometricForUser( + getContext(), getTargetUserId(), (Face) identifier); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 5727ffc468df..f39a5056f9e4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -32,6 +32,7 @@ import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; @@ -67,7 +68,9 @@ import android.os.Looper; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ResultReceiver; import android.os.ServiceManager; +import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -621,6 +624,15 @@ public class FingerprintService extends SystemService { } @Override // Binder call + public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, + @Nullable FileDescriptor err, @NonNull String[] args, + @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) + throws RemoteException { + (new FingerprintShellCommand(getContext(), FingerprintService.this)) + .exec(this, in, out, err, args, callback, resultReceiver); + } + + @Override // Binder call protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { return; @@ -1172,4 +1184,18 @@ public class FingerprintService extends SystemService { } return appOpsOk; } + + void syncEnrollmentsNow() { + Utils.checkPermissionOrShell(getContext(), MANAGE_FINGERPRINT); + if (Utils.isVirtualEnabled(getContext())) { + Slog.i(TAG, "Sync virtual enrollments"); + final int userId = ActivityManager.getCurrentUser(); + for (ServiceProvider provider : mServiceProviders) { + for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { + provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */, + true /* favorHalEnrollments */); + } + } + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java new file mode 100644 index 000000000000..636413f75cf5 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.fingerprint; + +import android.content.Context; +import android.os.ShellCommand; + +import java.io.PrintWriter; + +/** Handles shell commands for {@link FingerprintService}. */ +public class FingerprintShellCommand extends ShellCommand { + + private final Context mContext; + private final FingerprintService mService; + + public FingerprintShellCommand(Context context, FingerprintService service) { + mContext = context; + mService = service; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + onHelp(); + return 1; + } + + try { + switch (cmd) { + case "help": + return doHelp(); + case "sync": + return doSync(); + default: + getOutPrintWriter().println("Unrecognized command: " + cmd); + } + } catch (Exception e) { + getOutPrintWriter().println("Exception: " + e); + } + return -1; + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("Fingerprint Service commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" sync"); + pw.println(" Sync enrollments now (virtualized sensors only)."); + } + + private int doHelp() { + onHelp(); + return 0; + } + + private int doSync() { + mService.syncEnrollmentsNow(); + return 0; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 9cdbdc9158fb..4dfc73872014 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -123,6 +123,9 @@ public interface ServiceProvider { void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback); + void scheduleInternalCleanup(int sensorId, int userId, + @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments); + boolean isHardwareDetected(int sensorId); void rename(int sensorId, int fingerId, int userId, @NonNull String name); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java index 09bdd6de49f0..c315ccf8dea2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java @@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; @@ -72,4 +73,11 @@ class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint utils, sensorId, logger.swapAction(context, BiometricsProtoEnums.ACTION_REMOVE), biometricContext, authenticatorIds); } + + @Override + protected void onAddUnknownTemplate(int userId, + @NonNull BiometricAuthenticator.Identifier identifier) { + FingerprintUtils.getInstance(getSensorId()).addBiometricForUser( + getContext(), getTargetUserId(), (Fingerprint) identifier); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 1fac8a8ce5c9..e6887e63c877 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -516,6 +516,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback) { + scheduleInternalCleanup(sensorId, userId, callback, false /* favorHalEnrollments */); + } + + @Override + public void scheduleInternalCleanup(int sensorId, int userId, + @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) { mHandler.post(() -> { final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId); final FingerprintInternalCleanupClient client = @@ -527,6 +533,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mBiometricContext, enrolledList, FingerprintUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); + if (favorHalEnrollments) { + client.setFavorHalEnrollments(); + } scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback, mFingerprintStateCallback)); }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 1d2a3655021c..895fb4671337 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -757,6 +757,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mFingerprintStateCallback)); } + @Override + public void scheduleInternalCleanup(int sensorId, int userId, + @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) { + scheduleInternalCleanup(userId, new ClientMonitorCompositeCallback(callback, + mFingerprintStateCallback)); + } + private BiometricLogger createLogger(int statsAction, int statsClient) { return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT, statsAction, statsClient); diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index aab6281e6cd1..387e00f73932 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -20,9 +20,9 @@ import static android.Manifest.permission.LOG_COMPAT_CHANGE; import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD; import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.Process.SYSTEM_UID; +import android.annotation.EnforcePermission; +import android.annotation.RequiresNoPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IActivityManager; @@ -93,15 +93,15 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(LOG_COMPAT_CHANGE) public void reportChange(long changeId, ApplicationInfo appInfo) { - checkCompatChangeLogPermission(); reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED); } @Override + @EnforcePermission(LOG_COMPAT_CHANGE) public void reportChangeByPackageName(long changeId, String packageName, @UserIdInt int userId) { - checkCompatChangeLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo != null) { reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED); @@ -109,8 +109,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(LOG_COMPAT_CHANGE) public void reportChangeByUid(long changeId, int uid) { - checkCompatChangeLogPermission(); reportChangeInternal(changeId, uid, ChangeReporter.STATE_LOGGED); } @@ -119,15 +119,15 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { - checkCompatChangeReadAndLogPermission(); return isChangeEnabledInternal(changeId, appInfo); } @Override + @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) public boolean isChangeEnabledByPackageName(long changeId, String packageName, @UserIdInt int userId) { - checkCompatChangeReadAndLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return mCompatConfig.willChangeBeEnabled(changeId, packageName); @@ -136,8 +136,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) public boolean isChangeEnabledByUid(long changeId, int uid) { - checkCompatChangeReadAndLogPermission(); String[] packages = mContext.getPackageManager().getPackagesForUid(uid); if (packages == null || packages.length == 0) { return mCompatConfig.defaultChangeIdValue(changeId); @@ -197,8 +197,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public void setOverrides(CompatibilityChangeConfig overrides, String packageName) { - checkCompatChangeOverridePermission(); Map<Long, PackageOverride> overridesMap = new HashMap<>(); for (long change : overrides.enabledChanges()) { overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build()); @@ -213,8 +213,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) { - checkCompatChangeOverridePermission(); Map<Long, PackageOverride> overridesMap = new HashMap<>(); for (long change : overrides.enabledChanges()) { overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build()); @@ -228,9 +228,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public void putAllOverridesOnReleaseBuilds( CompatibilityOverridesByPackageConfig overridesByPackage) { - checkCompatChangeOverrideOverridablePermission(); for (CompatibilityOverrideConfig overrides : overridesByPackage.packageNameToOverrides.values()) { checkAllCompatOverridesAreOverridable(overrides.overrides.keySet()); @@ -239,16 +239,16 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public void putOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides, String packageName) { - checkCompatChangeOverrideOverridablePermission(); checkAllCompatOverridesAreOverridable(overrides.overrides.keySet()); mCompatConfig.addPackageOverrides(overrides, packageName, /* skipUnknownChangeIds= */ true); } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public int enableTargetSdkChanges(String packageName, int targetSdkVersion) { - checkCompatChangeOverridePermission(); int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName, targetSdkVersion); killPackage(packageName); @@ -256,8 +256,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public int disableTargetSdkChanges(String packageName, int targetSdkVersion) { - checkCompatChangeOverridePermission(); int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName, targetSdkVersion); killPackage(packageName); @@ -265,36 +265,36 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public void clearOverrides(String packageName) { - checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); killPackage(packageName); } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public void clearOverridesForTest(String packageName) { - checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public boolean clearOverride(long changeId, String packageName) { - checkCompatChangeOverridePermission(); boolean existed = mCompatConfig.removeOverride(changeId, packageName); killPackage(packageName); return existed; } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG) public boolean clearOverrideForTest(long changeId, String packageName) { - checkCompatChangeOverridePermission(); return mCompatConfig.removeOverride(changeId, packageName); } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public void removeAllOverridesOnReleaseBuilds( CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage) { - checkCompatChangeOverrideOverridablePermission(); for (CompatibilityOverridesToRemoveConfig overridesToRemove : overridesToRemoveByPackage.packageNameToOverridesToRemove.values()) { checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds); @@ -303,27 +303,28 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public void removeOverridesOnReleaseBuilds( CompatibilityOverridesToRemoveConfig overridesToRemove, String packageName) { - checkCompatChangeOverrideOverridablePermission(); checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds); mCompatConfig.removePackageOverrides(overridesToRemove, packageName); } @Override + @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG}) public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { - checkCompatChangeReadAndLogPermission(); return mCompatConfig.getAppConfig(appInfo); } @Override + @EnforcePermission(READ_COMPAT_CHANGE_CONFIG) public CompatibilityChangeInfo[] listAllChanges() { - checkCompatChangeReadPermission(); return mCompatConfig.dumpChanges(); } @Override + @RequiresNoPermission public CompatibilityChangeInfo[] listUIChanges() { return Arrays.stream(listAllChanges()).filter(this::isShownInUI).toArray( CompatibilityChangeInfo[]::new); @@ -362,11 +363,15 @@ public class PlatformCompat extends IPlatformCompat.Stub { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) { return; } - checkCompatChangeReadAndLogPermission(); + mContext.enforceCallingOrSelfPermission( + READ_COMPAT_CHANGE_CONFIG, "Cannot read compat change"); + mContext.enforceCallingOrSelfPermission( + LOG_COMPAT_CHANGE, "Cannot read log compat change usage"); mCompatConfig.dumpConfig(pw); } @Override + @RequiresNoPermission public IOverrideValidator getOverrideValidator() { return mCompatConfig.getOverrideValidator(); } @@ -414,49 +419,6 @@ public class PlatformCompat extends IPlatformCompat.Stub { } } - private void checkCompatChangeLogPermission() throws SecurityException { - // Don't check for permissions within the system process - if (Binder.getCallingUid() == SYSTEM_UID) { - return; - } - if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) != PERMISSION_GRANTED) { - throw new SecurityException("Cannot log compat change usage"); - } - } - - private void checkCompatChangeReadPermission() { - // Don't check for permissions within the system process - if (Binder.getCallingUid() == SYSTEM_UID) { - return; - } - if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG) - != PERMISSION_GRANTED) { - throw new SecurityException("Cannot read compat change"); - } - } - - private void checkCompatChangeOverridePermission() { - // Don't check for permissions within the system process - if (Binder.getCallingUid() == SYSTEM_UID) { - return; - } - if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG) - != PERMISSION_GRANTED) { - throw new SecurityException("Cannot override compat change"); - } - } - - private void checkCompatChangeOverrideOverridablePermission() { - // Don't check for permissions within the system process - if (Binder.getCallingUid() == SYSTEM_UID) { - return; - } - if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) - != PERMISSION_GRANTED) { - throw new SecurityException("Cannot override compat change"); - } - } - private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) { for (Long changeId : changeIds) { if (isKnownChangeId(changeId) && !mCompatConfig.isOverridable(changeId)) { @@ -466,11 +428,6 @@ public class PlatformCompat extends IPlatformCompat.Stub { } } - private void checkCompatChangeReadAndLogPermission() { - checkCompatChangeReadPermission(); - checkCompatChangeLogPermission(); - } - private boolean isShownInUI(CompatibilityChangeInfo change) { if (change.getLoggingOnly()) { return false; diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index edaa18a88637..a6613589cbc4 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -173,7 +173,7 @@ final class DisplayDeviceInfo { public static final int TOUCH_VIRTUAL = 3; /** - * Diff result: The {@link #state} fields differ. + * Diff result: The {@link #state} or {@link #committedState} fields differ. */ public static final int DIFF_STATE = 1 << 0; @@ -335,6 +335,13 @@ final class DisplayDeviceInfo { public int state = Display.STATE_ON; /** + * Display committed state. + * + * This matches {@link DisplayDeviceInfo#state} only after the power state change finishes. + */ + public int committedState = Display.STATE_UNKNOWN; + + /** * The UID of the application that owns this display, or zero if it is owned by the system. * <p> * If the display is private, then only the owner can use it. @@ -387,7 +394,7 @@ final class DisplayDeviceInfo { */ public int diff(DisplayDeviceInfo other) { int diff = 0; - if (state != other.state) { + if (state != other.state || committedState != other.committedState) { diff |= DIFF_STATE; } if (colorMode != other.colorMode) { @@ -461,6 +468,7 @@ final class DisplayDeviceInfo { address = other.address; deviceProductInfo = other.deviceProductInfo; state = other.state; + committedState = other.committedState; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; frameRateOverrides = other.frameRateOverrides; @@ -501,6 +509,7 @@ final class DisplayDeviceInfo { } sb.append(", deviceProductInfo ").append(deviceProductInfo); sb.append(", state ").append(Display.stateToString(state)); + sb.append(", committedState ").append(Display.stateToString(committedState)); if (ownerUid != 0 || ownerPackageName != null) { sb.append(", owner ").append(ownerPackageName); sb.append(" (uid ").append(ownerUid).append(")"); diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 0b9d4debd16f..f98c7dff97e3 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -42,8 +42,8 @@ import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; import com.android.server.display.DisplayManagerService.Clock; import java.io.PrintWriter; +import java.util.ArrayDeque; import java.util.Iterator; -import java.util.LinkedList; /** * Controls the status of high-brightness mode for devices that support it. This class assumes that @@ -110,11 +110,11 @@ class HighBrightnessModeController { private long mRunningStartTimeMillis = -1; /** - * List of previous HBM-events ordered from most recent to least recent. + * Queue of previous HBM-events ordered from most recent to least recent. * Meant to store only the events that fall into the most recent - * {@link mHbmData.timeWindowMillis}. + * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}. */ - private LinkedList<HbmEvent> mEvents = new LinkedList<>(); + private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>(); HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax, @@ -234,7 +234,7 @@ class HighBrightnessModeController { mRunningStartTimeMillis = -1; if (DEBUG) { - Slog.d(TAG, "New HBM event: " + mEvents.getFirst()); + Slog.d(TAG, "New HBM event: " + mEvents.peekFirst()); } } } @@ -433,7 +433,7 @@ class HighBrightnessModeController { // window by at least minTime. Basically, we're calculating the soonest time we can // get {@code timeMinMillis} back to us. final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis; - final HbmEvent lastEvent = mEvents.getLast(); + final HbmEvent lastEvent = mEvents.peekLast(); final long startTimePlusMinMillis = Math.max(windowstartTimeMillis, lastEvent.startTimeMillis) + mHbmData.timeMinMillis; diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 982ac3c84482..bb78e73aa4fe 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -197,6 +197,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayDeviceInfo mInfo; private boolean mHavePendingChanges; private int mState = Display.STATE_UNKNOWN; + private int mCommittedState = Display.STATE_UNKNOWN; + // This is only set in the runnable returned from requestDisplayStateLocked. private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; @@ -637,6 +639,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos; mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos; mInfo.state = mState; + mInfo.committedState = mCommittedState; mInfo.uniqueId = getUniqueId(); final DisplayAddress.Physical physicalAddress = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId); @@ -817,6 +820,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } + setCommittedState(state); // If we're entering a suspended (but not OFF) power state and we // have a sidekick available, tell it now that it can take control. if (Display.isSuspendedState(state) && state != Display.STATE_OFF @@ -831,6 +835,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + private void setCommittedState(int state) { + // After the display state is set, let's update the committed state. + getHandler().post(() -> { + synchronized (getSyncRoot()) { + mCommittedState = state; + updateDeviceInfoLocked(); + } + }); + } + private void setDisplayBrightness(float brightnessState, float sdrBrightnessState) { // brightnessState includes invalid, off and full range. @@ -1096,6 +1110,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mDefaultModeId=" + mDefaultModeId); pw.println("mUserPreferredModeId=" + mUserPreferredModeId); pw.println("mState=" + Display.stateToString(mState)); + pw.println("mCommittedState=" + Display.stateToString(mCommittedState)); pw.println("mBrightnessState=" + mBrightnessState); pw.println("mBacklightAdapter=" + mBacklightAdapter); pw.println("mAllmSupported=" + mAllmSupported); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index a640497d7e10..839555b32181 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -413,6 +413,7 @@ final class LogicalDisplay { mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos; mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos; mBaseDisplayInfo.state = deviceInfo.state; + mBaseDisplayInfo.committedState = deviceInfo.committedState; mBaseDisplayInfo.smallestNominalAppWidth = maskedWidth; mBaseDisplayInfo.smallestNominalAppHeight = maskedHeight; mBaseDisplayInfo.largestNominalAppWidth = maskedWidth; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 576a5ff4305e..26e38bdb6d51 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -306,16 +306,23 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @Override @ServiceThreadOnly protected void onInitializeCecComplete(int initiatedBy) { - if (initiatedBy == HdmiControlService.INITIATED_BY_SCREEN_ON) { - oneTouchPlay(new IHdmiControlCallback.Stub() { - @Override - public void onComplete(int result) { - if (result != HdmiControlManager.RESULT_SUCCESS) { - Slog.w(TAG, "Failed to complete One Touch Play. result=" + result); - } - } - }); + if (initiatedBy != HdmiControlService.INITIATED_BY_SCREEN_ON) { + return; } + @HdmiControlManager.PowerControlMode + String powerControlMode = mService.getHdmiCecConfig().getStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); + if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_NONE)) { + return; + } + oneTouchPlay(new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + if (result != HdmiControlManager.RESULT_SUCCESS) { + Slog.w(TAG, "Failed to complete One Touch Play. result=" + result); + } + } + }); } @Override diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 8ab0b931be11..e433324b152d 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -16,6 +16,7 @@ package com.android.server.input; +import static android.provider.DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT; import static android.view.KeyEvent.KEYCODE_UNKNOWN; import android.annotation.NonNull; @@ -157,6 +158,8 @@ public class InputManagerService extends IInputManager.Stub // Feature flag name for the deep press feature private static final String DEEP_PRESS_ENABLED = "deep_press_enabled"; + // Feature flag name for the strategy to be used in VelocityTracker + private static final String VELOCITYTRACKER_STRATEGY_PROPERTY = "velocitytracker_strategy"; private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1; private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2; @@ -358,6 +361,8 @@ public class InputManagerService extends IInputManager.Stub public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER; public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE; + private final String mVelocityTrackerStrategy; + /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */ final boolean mUseDevInputEventForAudioJack; @@ -406,6 +411,8 @@ public class InputManagerService extends IInputManager.Stub mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null : new File(doubleTouchGestureEnablePath); + mVelocityTrackerStrategy = DeviceConfig.getProperty( + NAMESPACE_INPUT_NATIVE_BOOT, VELOCITYTRACKER_STRATEGY_PROPERTY); LocalServices.addService(InputManagerInternal.class, new LocalService()); } @@ -878,6 +885,11 @@ public class InputManagerService extends IInputManager.Stub return mNative.verifyInputEvent(event); } + @Override // Binder call + public String getVelocityTrackerStrategy() { + return mVelocityTrackerStrategy; + } + /** * Gets information about the input device with the specified id. * @param deviceId The device id. @@ -2343,7 +2355,7 @@ public class InputManagerService extends IInputManager.Stub public void removePortAssociation(@NonNull String inputPort) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, - "clearPortAssociations()")) { + "removePortAssociation()")) { throw new SecurityException( "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); } @@ -2359,7 +2371,7 @@ public class InputManagerService extends IInputManager.Stub public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, - "addNameAssociation()")) { + "addUniqueIdAssociation()")) { throw new SecurityException( "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3c3140551bc3..8cf0ab744124 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4734,7 +4734,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void hideMySoftInput(@NonNull IBinder token, int flags) { + private void hideMySoftInput(@NonNull IBinder token, int flags, + @SoftInputShowHideReason int reason) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput"); synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { @@ -4742,10 +4743,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } final long ident = Binder.clearCallingIdentity(); try { - hideCurrentInputLocked( - mLastImeTargetWindow, flags, null, - SoftInputShowHideReason.HIDE_MY_SOFT_INPUT); - + hideCurrentInputLocked(mLastImeTargetWindow, flags, null, reason); } finally { Binder.restoreCallingIdentity(ident); } @@ -4763,7 +4761,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final long ident = Binder.clearCallingIdentity(); try { showCurrentInputLocked(mLastImeTargetWindow, flags, null, - SoftInputShowHideReason.SHOW_MY_SOFT_INPUT); + SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME); } finally { Binder.restoreCallingIdentity(ident); } @@ -6571,11 +6569,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void hideMySoftInput(int flags, AndroidFuture future /* T=Void */) { + public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason, + AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { - mImms.hideMySoftInput(mToken, flags); + mImms.hideMySoftInput(mToken, flags, reason); typedFuture.complete(null); } catch (Throwable e) { typedFuture.completeExceptionally(e); diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java index a9b2570a3dda..e09f7b09b4d1 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java @@ -40,11 +40,12 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Iterator; -import java.util.LinkedList; +import java.util.List; import java.util.Set; /** @@ -76,7 +77,7 @@ public class NotificationHistoryDatabase { private final Handler mFileWriteHandler; @VisibleForTesting // List of files holding history information, sorted newest to oldest - final LinkedList<AtomicFile> mHistoryFiles; + final List<AtomicFile> mHistoryFiles; private final File mHistoryDir; private final File mVersionFile; // Current version of the database files schema @@ -94,7 +95,7 @@ public class NotificationHistoryDatabase { mFileWriteHandler = fileWriteHandler; mVersionFile = new File(dir, "version"); mHistoryDir = new File(dir, "history"); - mHistoryFiles = new LinkedList<>(); + mHistoryFiles = new ArrayList<>(); mBuffer = new NotificationHistory(); mWriteBufferRunnable = new WriteBufferRunnable(); @@ -133,7 +134,7 @@ public class NotificationHistoryDatabase { safeParseLong(lhs.getName()))); for (File file : files) { - mHistoryFiles.addLast(new AtomicFile(file)); + mHistoryFiles.add(new AtomicFile(file)); } } @@ -411,7 +412,7 @@ public class NotificationHistoryDatabase { + file.getBaseFile().getAbsolutePath()); try { writeLocked(file, mBuffer); - mHistoryFiles.addFirst(file); + mHistoryFiles.add(0, file); mBuffer = new NotificationHistory(); scheduleDeletion(file.getBaseFile(), time, HISTORY_RETENTION_DAYS); diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index 903d02afc307..f3cb7fb6107c 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -19,6 +19,7 @@ package com.android.server.om; import static com.android.server.om.OverlayManagerService.DEBUG; import static com.android.server.om.OverlayManagerService.TAG; +import android.annotation.IntDef; import android.annotation.NonNull; import android.content.om.OverlayInfo; import android.content.om.OverlayableInfo; @@ -33,6 +34,8 @@ import android.util.Slog; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -56,6 +59,18 @@ final class IdmapManager { VENDOR_IS_Q_OR_LATER = isQOrLater; } + static final int IDMAP_NOT_EXIST = 0; + static final int IDMAP_IS_VERIFIED = 1; + static final int IDMAP_IS_MODIFIED = 1 << 1; + + @IntDef(flag = true, prefix = { "IDMAP_" }, value = { + IDMAP_NOT_EXIST, + IDMAP_IS_VERIFIED, + IDMAP_IS_MODIFIED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IdmapStatus {} + private final IdmapDaemon mIdmapDaemon; private final PackageManagerHelper mPackageManager; @@ -76,8 +91,14 @@ final class IdmapManager { /** * Creates the idmap for the target/overlay combination and returns whether the idmap file was * modified. + * @return the status of the specific idmap file. It's one of the following.<ul> + * <li>{@link #IDMAP_NOT_EXIST} means the idmap file is not existed.</li> + * <li>{@link #IDMAP_IS_VERIFIED} means the idmap file is verified by Idmap2d.</li> + * <li>{@link #IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED } means the idmap file is modified and + * verified by Idmap2d.</li> + * </ul>. */ - boolean createIdmap(@NonNull final AndroidPackage targetPackage, + @IdmapStatus int createIdmap(@NonNull final AndroidPackage targetPackage, @NonNull final AndroidPackage overlayPackage, String overlayBasePath, String overlayName, int userId) { if (DEBUG) { @@ -90,14 +111,15 @@ final class IdmapManager { boolean enforce = enforceOverlayable(overlayPackage); if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies, enforce, userId)) { - return false; + return IDMAP_IS_VERIFIED; } - return mIdmapDaemon.createIdmap(targetPath, overlayBasePath, overlayName, policies, - enforce, userId) != null; + final boolean idmapCreated = mIdmapDaemon.createIdmap(targetPath, overlayBasePath, + overlayName, policies, enforce, userId) != null; + return (idmapCreated) ? IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED : IDMAP_NOT_EXIST; } catch (Exception e) { Slog.w(TAG, "failed to generate idmap for " + targetPath + " and " + overlayBasePath, e); - return false; + return IDMAP_NOT_EXIST; } } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 8ecc607603a1..9ba1552eaa7f 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -1477,7 +1477,7 @@ public final class OverlayManagerService extends SystemService { targetPackageNames = pm.getTargetPackageNames(userId); } - final Map<String, OverlayPaths> pendingChanges = + final ArrayMap<String, OverlayPaths> pendingChanges = new ArrayMap<>(targetPackageNames.size()); synchronized (mLock) { final OverlayPaths frameworkOverlays = @@ -1493,6 +1493,9 @@ public final class OverlayManagerService extends SystemService { } final HashSet<String> updatedPackages = new HashSet<>(); + final HashSet<String> invalidPackages = new HashSet<>(); + pm.setEnabledOverlayPackages(userId, pendingChanges, updatedPackages, invalidPackages); + for (final String targetPackageName : targetPackageNames) { if (DEBUG) { Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" @@ -1500,11 +1503,10 @@ public final class OverlayManagerService extends SystemService { + "] userId=" + userId); } - if (!pm.setEnabledOverlayPackages( - userId, targetPackageName, pendingChanges.get(targetPackageName), - updatedPackages)) { - Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", - targetPackageName, userId)); + if (invalidPackages.contains(targetPackageName)) { + Slog.e(TAG, TextUtils.formatSimple( + "Failed to change enabled overlays for %s user %d", targetPackageName, + userId)); } } return new ArrayList<>(updatedPackages); diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 38781fad76fd..e15e65ab711e 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -24,6 +24,9 @@ import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED; import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.server.om.IdmapManager.IDMAP_IS_MODIFIED; +import static com.android.server.om.IdmapManager.IDMAP_IS_VERIFIED; +import static com.android.server.om.IdmapManager.IDMAP_NOT_EXIST; import static com.android.server.om.OverlayManagerService.DEBUG; import static com.android.server.om.OverlayManagerService.TAG; @@ -785,15 +788,18 @@ final class OverlayManagerServiceImpl { // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native // layers. final OverlayInfo updatedOverlayInfo = mSettings.getOverlayInfo(overlay, userId); + @IdmapManager.IdmapStatus int idmapStatus = IDMAP_NOT_EXIST; if (targetPackage != null && !("android".equals(info.getTargetPackageName()) && !isPackageConfiguredMutable(overlayPackage))) { - modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage, - updatedOverlayInfo.baseCodePath, overlay.getOverlayName(), userId); + idmapStatus = mIdmapManager.createIdmap(targetPackage, + overlayPackage, updatedOverlayInfo.baseCodePath, overlay.getOverlayName(), + userId); + modified |= (idmapStatus & IDMAP_IS_MODIFIED) != 0; } final @OverlayInfo.State int currentState = mSettings.getState(overlay, userId); final @OverlayInfo.State int newState = calculateNewState(updatedOverlayInfo, targetPackage, - userId, flags); + userId, flags, idmapStatus); if (currentState != newState) { if (DEBUG) { Slog.d(TAG, String.format("%s:%d: %s -> %s", @@ -808,7 +814,8 @@ final class OverlayManagerServiceImpl { } private @OverlayInfo.State int calculateNewState(@NonNull final OverlayInfo info, - @Nullable final AndroidPackage targetPackage, final int userId, final int flags) + @Nullable final AndroidPackage targetPackage, final int userId, final int flags, + @IdmapManager.IdmapStatus final int idmapStatus) throws OverlayManagerSettings.BadKeyException { if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) { return STATE_TARGET_IS_BEING_REPLACED; @@ -822,8 +829,10 @@ final class OverlayManagerServiceImpl { return STATE_MISSING_TARGET; } - if (!mIdmapManager.idmapExists(info)) { - return STATE_NO_IDMAP; + if ((idmapStatus & IDMAP_IS_VERIFIED) == 0) { + if (!mIdmapManager.idmapExists(info)) { + return STATE_NO_IDMAP; + } } final boolean enabled = mSettings.getEnabled(info.getOverlayIdentifier(), userId); diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index a91c55fb895d..e07e3c103f5f 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -119,16 +119,18 @@ public abstract class ApexManager { @Nullable public final String apexModuleName; public final File apexDirectory; public final File preInstalledApexPath; + public final boolean isFactory; private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) { - this(null, apexDirectory, preInstalledApexPath); + this(null, apexDirectory, preInstalledApexPath, true); } private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, - File preInstalledApexPath) { + File preInstalledApexPath, boolean isFactory) { this.apexModuleName = apexModuleName; this.apexDirectory = apexDirectory; this.preInstalledApexPath = preInstalledApexPath; + this.isFactory = isFactory; } private ActiveApexInfo(ApexInfo apexInfo) { @@ -136,7 +138,8 @@ public abstract class ApexManager { apexInfo.moduleName, new File(Environment.getApexDirectory() + File.separator + apexInfo.moduleName), - new File(apexInfo.preinstalledModulePath)); + new File(apexInfo.preinstalledModulePath), + apexInfo.isFactory); } } @@ -211,7 +214,7 @@ public abstract class ApexManager { * @return {@code true} if this package is pre-installed, {@code false} otherwise. */ public static boolean isFactory(@NonNull PackageInfo packageInfo) { - return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0; } /** @@ -503,7 +506,7 @@ public abstract class ApexManager { * @return {@code true} if this package is active, {@code false} otherwise. */ private static boolean isActive(PackageInfo packageInfo) { - return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; + return packageInfo.isActiveApex; } /** @@ -1150,7 +1153,7 @@ public abstract class ApexManager { // Installation was successful, time to update mAllPackagesCache synchronized (mLock) { if (isFactory(existingApexPkg)) { - existingApexPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED; + existingApexPkg.isActiveApex = false; mAllPackagesCache.add(finalApexPkg); } else { for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index c259797942a7..1bce0cd2e4ba 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -440,10 +440,6 @@ public interface Computer extends PackageDataSnapshot { boolean getBlockUninstallForUser(@NonNull String packageName, @UserIdInt int userId); @Nullable - SparseArray<int[]> getBroadcastAllowList(@NonNull String packageName, @UserIdInt int[] userIds, - boolean isInstantApp); - - @Nullable String getInstallerPackageName(@NonNull String packageName); @Nullable @@ -479,6 +475,16 @@ public interface Computer extends PackageDataSnapshot { boolean isPackageSignedByKeySetExactly(@NonNull String packageName, @NonNull KeySet ks); + /** + * See {@link AppsFilterSnapshot#getVisibilityAllowList(PackageStateInternal, int[], ArrayMap)} + */ + @Nullable + SparseArray<int[]> getVisibilityAllowLists(@NonNull String packageName, + @UserIdInt int[] userIds); + + /** + * See {@link AppsFilterSnapshot#getVisibilityAllowList(PackageStateInternal, int[], ArrayMap)} + */ @Nullable int[] getVisibilityAllowList(@NonNull String packageName, @UserIdInt int userId); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 80d61b593fd2..ed1be2478a9b 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -540,8 +540,13 @@ public class ComputerEngine implements Computer { && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); + final boolean resolveForStartNonExported = resolveForStart + && !ai.exported + && !isCallerSameApp(pkgName, filterCallingUid); final boolean blockNormalResolution = - !resolveForStart && !isTargetInstantApp && !isCallerInstantApp + (!resolveForStart || resolveForStartNonExported) + && !isTargetInstantApp + && !isCallerInstantApp && shouldFilterApplication( getPackageStateInternal(ai.applicationInfo.packageName, Process.SYSTEM_UID), filterCallingUid, userId); @@ -1835,9 +1840,6 @@ public class ComputerEngine implements Computer { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); - if (listUninstalled) { - list.addAll(mApexManager.getInactivePackages()); - } } } return new ParceledListSlice<>(list); @@ -4345,11 +4347,8 @@ public class ComputerEngine implements Computer { @Override public List<String> getAllPackages() { - // Allow iorapd to call this method. - if (Binder.getCallingUid() != Process.IORAPD_UID) { - PackageManagerServiceUtils.enforceSystemOrRootOrShell( - "getAllPackages is limited to privileged callers"); - } + PackageManagerServiceUtils.enforceSystemOrRootOrShell( + "getAllPackages is limited to privileged callers"); final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (canViewInstantApps(callingUid, callingUserId)) { @@ -5027,20 +5026,6 @@ public class ComputerEngine implements Computer { @Nullable @Override - public SparseArray<int[]> getBroadcastAllowList(@NonNull String packageName, - @UserIdInt int[] userIds, boolean isInstantApp) { - if (isInstantApp) { - return null; - } - PackageStateInternal setting = getPackageStateInternal(packageName, Process.SYSTEM_UID); - if (setting == null) { - return null; - } - return mAppsFilter.getVisibilityAllowList(setting, userIds, getPackageStates()); - } - - @Nullable - @Override public String getInstallerPackageName(@NonNull String packageName) { final int callingUid = Binder.getCallingUid(); final InstallSource installSource = getInstallSource(packageName, callingUid); @@ -5317,14 +5302,21 @@ public class ComputerEngine implements Computer { @Nullable @Override - public int[] getVisibilityAllowList(@NonNull String packageName, @UserIdInt int userId) { + public SparseArray<int[]> getVisibilityAllowLists(@NonNull String packageName, + @UserIdInt int[] userIds) { final PackageStateInternal ps = getPackageStateInternal(packageName, Process.SYSTEM_UID); if (ps == null) { return null; } - final SparseArray<int[]> visibilityAllowList = mAppsFilter.getVisibilityAllowList(ps, - new int[]{userId}, getPackageStates()); + return mAppsFilter.getVisibilityAllowList(ps, userIds, getPackageStates()); + } + + @Nullable + @Override + public int[] getVisibilityAllowList(@NonNull String packageName, @UserIdInt int userId) { + final SparseArray<int[]> visibilityAllowList = getVisibilityAllowLists(packageName, + new int[]{userId}); return visibilityAllowList != null ? visibilityAllowList.get(userId) : null; } diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index d3d291ea52ac..7a490dcd8410 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -39,7 +39,6 @@ import android.app.ApplicationPackageManager; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDeleteObserver2; -import android.content.pm.PackageChangeEvent; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; @@ -772,7 +771,6 @@ final class DeletePackageHelper { } catch (RemoteException e) { Log.i(TAG, "Observer no longer exists."); } //end catch - notifyPackageChangeObserversOnDelete(packageName, versionCode); // Prune unused static shared libraries which have been cached a period of time mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */); @@ -832,18 +830,6 @@ final class DeletePackageHelper { return result; } - private void notifyPackageChangeObserversOnDelete(String packageName, long version) { - PackageChangeEvent pkgChangeEvent = new PackageChangeEvent(); - pkgChangeEvent.packageName = packageName; - pkgChangeEvent.version = version; - pkgChangeEvent.lastUpdateTimeMillis = 0L; - pkgChangeEvent.newInstalled = false; - pkgChangeEvent.dataRemoved = false; - pkgChangeEvent.isDeleted = true; - - mPm.notifyPackageChangeObservers(pkgChangeEvent); - } - private static class TempUserState { public final int enabledState; @Nullable diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java index 7dc45b58a773..b28b73e979fe 100644 --- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java +++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.RESTRICTION_NONE; +import static android.os.Process.SYSTEM_UID; import android.annotation.NonNull; import android.content.Intent; @@ -27,11 +28,13 @@ import android.os.UserHandle; import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.util.ArrayUtils; import com.android.server.pm.pkg.PackageStateInternal; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -126,7 +129,7 @@ public final class DistractingPackageHelper { if (!changedPackagesList.isEmpty()) { final String[] changedPackages = changedPackagesList.toArray( new String[changedPackagesList.size()]); - sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId, + sendDistractingPackagesChanged(snapshot, changedPackages, changedUids.toArray(), userId, restrictionFlags); mPm.scheduleWritePackageRestrictions(userId); } @@ -168,7 +171,7 @@ public final class DistractingPackageHelper { if (!changedPackages.isEmpty()) { final String[] packageArray = changedPackages.toArray( new String[changedPackages.size()]); - sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId, + sendDistractingPackagesChanged(snapshot, packageArray, changedUids.toArray(), userId, RESTRICTION_NONE); mPm.scheduleWritePackageRestrictions(userId); } @@ -181,18 +184,53 @@ public final class DistractingPackageHelper { * @param uidList The uids of packages which have suspension changes. * @param userId The user where packages reside. */ - void sendDistractingPackagesChanged(@NonNull String[] pkgList, + void sendDistractingPackagesChanged(@NonNull Computer snapshot, @NonNull String[] pkgList, int[] uidList, int userId, int distractionFlags) { - final Bundle extras = new Bundle(3); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); - extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); - extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); + final List<List<String>> pkgsToSend = new ArrayList(pkgList.length); + final List<IntArray> uidsToSend = new ArrayList(pkgList.length); + final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length); + final int[] userIds = new int[] {userId}; + // Get allow lists for the pkg in the pkgList. Merge into the existed pkgs and uids if + // allow lists are the same. + for (int i = 0; i < pkgList.length; i++) { + final String pkgName = pkgList[i]; + final int uid = uidList[i]; + SparseArray<int[]> allowList = mInjector.getAppsFilter().getVisibilityAllowList( + snapshot.getPackageStateInternal(pkgName, SYSTEM_UID), + userIds, snapshot.getPackageStates()); + if (allowList == null) { + allowList = new SparseArray<>(0); + } + boolean merged = false; + for (int j = 0; j < allowListsToSend.size(); j++) { + if (Arrays.equals(allowListsToSend.get(j).get(userId), allowList.get(userId))) { + pkgsToSend.get(j).add(pkgName); + uidsToSend.get(j).add(uid); + merged = true; + break; + } + } + if (!merged) { + pkgsToSend.add(new ArrayList<>(Arrays.asList(pkgName))); + uidsToSend.add(IntArray.wrap(new int[] {uid})); + allowListsToSend.add(allowList); + } + } final Handler handler = mInjector.getHandler(); - handler.post(() -> mBroadcastHelper.sendPackageBroadcast( - Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, extras, - Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */, - null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */, - null /* allowList */, null /* bOptions */)); + for (int i = 0; i < pkgsToSend.size(); i++) { + final Bundle extras = new Bundle(3); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, + pkgsToSend.get(i).toArray(new String[pkgsToSend.get(i).size()])); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray()); + extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); + final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0 + ? null : allowListsToSend.get(i); + handler.post(() -> mBroadcastHelper.sendPackageBroadcast( + Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, + extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */, + null /* finishedReceiver */, userIds, null /* instantUserIds */, + allowList, null /* bOptions */)); + } } } diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 154f32a2e436..3dd0022c28cf 100644 --- a/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -21,6 +21,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; +import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY; import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED; import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM; import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; @@ -31,6 +32,7 @@ import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN; import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX; +import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS; import android.annotation.NonNull; import android.annotation.Nullable; @@ -167,7 +169,11 @@ final class InitAppsHelper { sp.getFolder().getAbsolutePath()) || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( sp.getFolder().getAbsolutePath() + File.separator)) { - return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); + int additionalScanFlag = SCAN_AS_APK_IN_APEX; + if (apexInfo.isFactory) { + additionalScanFlag |= SCAN_AS_FACTORY; + } + return new ScanPartition(apexInfo.apexDirectory, sp, additionalScanFlag); } } return null; @@ -317,8 +323,9 @@ final class InitAppsHelper { packageParser, executorService); } - scanDirTracedLI(frameworkDir, null, - mSystemParseFlags, + List<File> frameworkSplits = getFrameworkResApkSplitFiles(); + scanDirTracedLI(frameworkDir, frameworkSplits, + mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, packageParser, executorService); if (!mPm.mPackages.containsKey("android")) { diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index bbdb7ebfe887..5880332d9cd5 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -60,6 +60,7 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.POST_INSTALL; import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; +import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY; import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP; import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM; @@ -101,7 +102,6 @@ import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.DataLoaderType; import android.content.pm.IPackageInstallObserver2; -import android.content.pm.PackageChangeEvent; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; @@ -318,6 +318,12 @@ final class InstallPackageHelper { pkgSetting.setInstallSource(installSource); } + if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { + boolean isFactory = (scanFlags & SCAN_AS_FACTORY) != 0; + pkgSetting.getPkgState().setApkInApex(true); + pkgSetting.getPkgState().setApkInUpdatedApex(!isFactory); + } + // TODO(toddke): Consider a method specifically for modifying the Package object // post scan; or, moving this stuff out of the Package object since it has nothing // to do with the package on disk. @@ -2344,29 +2350,11 @@ final class InstallPackageHelper { // BackgroundDexOptService will remove it from its denylist. // TODO: Layering violation BackgroundDexOptService.getService().notifyPackageChanged(packageName); - - notifyPackageChangeObserversOnUpdate(reconciledPkg); } PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental( incrementalStorages); } - private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) { - final PackageSetting pkgSetting = reconciledPkg.mPkgSetting; - final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.mInstallResult; - final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo; - - PackageChangeEvent pkgChangeEvent = new PackageChangeEvent(); - pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName(); - pkgChangeEvent.version = pkgSetting.getVersionCode(); - pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime(); - pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate); - pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved); - pkgChangeEvent.isDeleted = false; - - mPm.notifyPackageChangeObservers(pkgChangeEvent); - } - public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) { String packageName = pkgLite.packageName; int installLocation = pkgLite.installLocation; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 7c900ef9401a..3e314a4e0f63 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -299,6 +299,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final SessionParams params; final long createdMillis; + /** Used for tracking whether user action was required for an install. */ + @Nullable + private Boolean mUserActionRequired; + /** Staging location where client data is written. */ final File stageDir; final String stageCid; @@ -2131,8 +2135,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * in its belong session set. When the user answers the yes, * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is * handled to come back here to check again. + * + * {@code mUserActionRequired} is used to track when user action is required for an + * install. Since control may come back here more than 1 time, we must ensure that it's + * value is not overwritten. */ - if (sendPendingUserActionIntentIfNeeded()) { + boolean wasUserActionIntentSent = sendPendingUserActionIntentIfNeeded(); + if (mUserActionRequired == null) { + mUserActionRequired = wasUserActionIntentSent; + } + if (wasUserActionIntentSent) { return; } @@ -3323,6 +3335,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + /** + * @return a boolean value indicating whether user action was requested for the install. + * Returns {@code false} if {@code mUserActionRequired} is {@code null} + */ + public boolean getUserActionRequired() { + if (mUserActionRequired != null) { + return mUserActionRequired.booleanValue(); + } + Slog.wtf(TAG, "mUserActionRequired should not be null."); + return false; + } + private static String getRelativePath(File file, File base) throws IOException { final String pathStr = file.getAbsolutePath(); final String baseStr = base.getAbsolutePath(); diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java index 9a43008acfdf..77d2ec970567 100644 --- a/services/core/java/com/android/server/pm/PackageManagerNative.java +++ b/services/core/java/com/android/server/pm/PackageManagerNative.java @@ -20,20 +20,16 @@ import static android.content.pm.PackageManager.CERT_INPUT_SHA256; import static com.android.server.pm.PackageManagerService.TAG; -import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageManagerNative; import android.content.pm.IStagedApexObserver; import android.content.pm.PackageInfo; import android.content.pm.StagedApexInfo; import android.os.Binder; -import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; -import android.util.Log; import android.util.Slog; import java.util.Arrays; @@ -46,35 +42,6 @@ final class PackageManagerNative extends IPackageManagerNative.Stub { } @Override - public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) { - synchronized (mPm.mPackageChangeObservers) { - try { - observer.asBinder().linkToDeath( - new PackageChangeObserverDeathRecipient(observer), 0); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); - } - mPm.mPackageChangeObservers.add(observer); - Log.d(TAG, "Size of mPackageChangeObservers after registry is " - + mPm.mPackageChangeObservers.size()); - } - } - - @Override - public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) { - synchronized (mPm.mPackageChangeObservers) { - mPm.mPackageChangeObservers.remove(observer); - Log.d(TAG, "Size of mPackageChangeObservers after unregistry is " - + mPm.mPackageChangeObservers.size()); - } - } - - @Override - public String[] getAllPackages() { - return mPm.snapshotComputer().getAllPackages().toArray(new String[0]); - } - - @Override public String[] getNamesForUids(int[] uids) throws RemoteException { String[] names = null; String[] results = null; @@ -222,21 +189,4 @@ final class PackageManagerNative extends IPackageManagerNative.Stub { public StagedApexInfo getStagedApexInfo(String moduleName) { return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName); } - - private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient { - private final IPackageChangeObserver mObserver; - - PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) { - mObserver = observer; - } - - @Override - public void binderDied() { - synchronized (mPm.mPackageChangeObservers) { - mPm.mPackageChangeObservers.remove(mObserver); - Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is " - + mPm.mPackageChangeObservers.size()); - } - } - } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ffd924edc7d8..6e51bc9af5eb 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -80,7 +80,6 @@ import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.IDexModuleRegisterCallback; import android.content.pm.IOnChecksumsReadyListener; -import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver2; import android.content.pm.IPackageInstallObserver2; @@ -92,7 +91,6 @@ import android.content.pm.InstallSourceInfo; import android.content.pm.InstantAppInfo; import android.content.pm.InstantAppRequest; import android.content.pm.ModuleInfo; -import android.content.pm.PackageChangeEvent; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; @@ -376,6 +374,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService static final int SCAN_AS_SYSTEM_EXT = 1 << 21; static final int SCAN_AS_ODM = 1 << 22; static final int SCAN_AS_APK_IN_APEX = 1 << 23; + static final int SCAN_AS_FACTORY = 1 << 24; @IntDef(flag = true, prefix = { "SCAN_" }, value = { SCAN_NO_DEX, @@ -690,10 +689,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService private @NonNull final OverlayConfig mOverlayConfig; - @GuardedBy("itself") - final ArrayList<IPackageChangeObserver> mPackageChangeObservers = - new ArrayList<>(); - // Cached parsed flag value. Invalidated on each flag change. PerUidReadTimeouts[] mPerUidReadTimeoutsCache; @@ -1843,10 +1838,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService mDexOptHelper = new DexOptHelper(this); mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper, mProtectedPackages); - mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper, - mRemovePackageHelper); mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper, mSuspendPackageHelper); + mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper, + mRemovePackageHelper); synchronized (mLock) { // Create the computer as soon as the state objects have been installed. The @@ -3142,23 +3137,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService }); } - void notifyPackageChangeObservers(PackageChangeEvent event) { - try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers"); - synchronized (mPackageChangeObservers) { - for (IPackageChangeObserver observer : mPackageChangeObservers) { - try { - observer.onPackageChanged(event); - } catch (RemoteException e) { - Log.wtf(TAG, e); - } - } - } - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - @SuppressWarnings("GuardedBy") VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) { if (pkg.isExternalStorage()) { @@ -3962,8 +3940,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID); final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; - final SparseArray<int[]> broadcastAllowList = snapshot.getBroadcastAllowList( - packageName, userIds, isInstantApp); + final SparseArray<int[]> broadcastAllowList = + isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds); mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast( packageName, dontKillApp, componentNames, packageUid, reason, userIds, instantUserIds, broadcastAllowList)); @@ -4988,8 +4966,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String getSplashScreenTheme(@NonNull String packageName, int userId) { final Computer snapshot = snapshotComputer(); + final int callingUid = Binder.getCallingUid(); + snapshot.enforceCrossUserPermission( + callingUid, userId, false /* requireFullPermission */, + false /* checkShell */, "getSplashScreenTheme"); PackageStateInternal packageState = filterPackageStateForInstalledAndFiltered(snapshot, - packageName, Binder.getCallingUid(), userId); + packageName, callingUid, userId); return packageState == null ? null : packageState.getUserStateOrDefault(userId).getSplashScreenTheme(); } @@ -6278,11 +6260,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override - public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName, - @Nullable OverlayPaths overlayPaths, - @NonNull Set<String> outUpdatedPackageNames) { - return PackageManagerService.this.setEnabledOverlayPackages(userId, targetPackageName, - overlayPaths, outUpdatedPackageNames); + public void setEnabledOverlayPackages(int userId, + @NonNull ArrayMap<String, OverlayPaths> pendingChanges, + @NonNull Set<String> outUpdatedPackageNames, + @NonNull Set<String> outInvalidPackageNames) { + PackageManagerService.this.setEnabledOverlayPackages(userId, + pendingChanges, outUpdatedPackageNames, outInvalidPackageNames); } @Override @@ -6492,85 +6475,119 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } - private boolean setEnabledOverlayPackages(@UserIdInt int userId, - @NonNull String targetPackageName, @Nullable OverlayPaths newOverlayPaths, - @NonNull Set<String> outUpdatedPackageNames) { + private void setEnabledOverlayPackages(@UserIdInt int userId, + @NonNull ArrayMap<String, OverlayPaths> pendingChanges, + @NonNull Set<String> outUpdatedPackageNames, + @NonNull Set<String> outInvalidPackageNames) { + final ArrayMap<String, ArrayMap<String, ArraySet<String>>> + targetPkgToLibNameToModifiedDependents = new ArrayMap<>(); + final int numberOfPendingChanges = pendingChanges.size(); + synchronized (mOverlayPathsLock) { - final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents = new ArrayMap<>(); Computer computer = snapshotComputer(); - final PackageStateInternal packageState = computer.getPackageStateInternal( - targetPackageName); - final AndroidPackage targetPkg = packageState == null ? null : packageState.getPkg(); - if (targetPackageName == null || targetPkg == null) { - Slog.e(TAG, "failed to find package " + targetPackageName); - return false; - } + for (int i = 0; i < numberOfPendingChanges; i++) { + final String targetPackageName = pendingChanges.keyAt(i); + final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i); + final PackageStateInternal packageState = computer.getPackageStateInternal( + targetPackageName); + final AndroidPackage targetPkg = + packageState == null ? null : packageState.getPkg(); + if (targetPackageName == null || targetPkg == null) { + Slog.e(TAG, "failed to find package " + targetPackageName); + outInvalidPackageNames.add(targetPackageName); + continue; + } - if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(), - newOverlayPaths)) { - return true; - } + if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(), + newOverlayPaths)) { + continue; + } - if (targetPkg.getLibraryNames() != null) { - // Set the overlay paths for dependencies of the shared library. - for (final String libName : targetPkg.getLibraryNames()) { - ArraySet<String> modifiedDependents = null; + if (targetPkg.getLibraryNames() != null) { + // Set the overlay paths for dependencies of the shared library. + for (final String libName : targetPkg.getLibraryNames()) { + ArraySet<String> modifiedDependents = null; - final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName, - SharedLibraryInfo.VERSION_UNDEFINED); - if (info == null) { - continue; - } - final List<VersionedPackage> dependents = computer - .getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID, userId); - if (dependents == null) { - continue; - } - for (final VersionedPackage dependent : dependents) { - final PackageStateInternal dependentState = - computer.getPackageStateInternal(dependent.getPackageName()); - if (dependentState == null) { + final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName, + SharedLibraryInfo.VERSION_UNDEFINED); + if (info == null) { continue; } - if (!Objects.equals(dependentState.getUserStateOrDefault(userId) - .getSharedLibraryOverlayPaths() - .get(libName), newOverlayPaths)) { - String dependentPackageName = dependent.getPackageName(); - modifiedDependents = ArrayUtils.add(modifiedDependents, - dependentPackageName); - outUpdatedPackageNames.add(dependentPackageName); + final List<VersionedPackage> dependents = + computer.getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID, + userId); + if (dependents == null) { + continue; + } + for (final VersionedPackage dependent : dependents) { + final PackageStateInternal dependentState = + computer.getPackageStateInternal(dependent.getPackageName()); + if (dependentState == null) { + continue; + } + if (!Objects.equals(dependentState.getUserStateOrDefault(userId) + .getSharedLibraryOverlayPaths() + .get(libName), newOverlayPaths)) { + String dependentPackageName = dependent.getPackageName(); + modifiedDependents = ArrayUtils.add(modifiedDependents, + dependentPackageName); + outUpdatedPackageNames.add(dependentPackageName); + } } - } - if (modifiedDependents != null) { - libNameToModifiedDependents.put(libName, modifiedDependents); + if (modifiedDependents != null) { + ArrayMap<String, ArraySet<String>> libNameToModifiedDependents = + targetPkgToLibNameToModifiedDependents.get( + targetPackageName); + if (libNameToModifiedDependents == null) { + libNameToModifiedDependents = new ArrayMap<>(); + targetPkgToLibNameToModifiedDependents.put(targetPackageName, + libNameToModifiedDependents); + } + libNameToModifiedDependents.put(libName, modifiedDependents); + } } } - } - outUpdatedPackageNames.add(targetPackageName); + outUpdatedPackageNames.add(targetPackageName); + } commitPackageStateMutation(null, mutator -> { - mutator.forPackage(targetPackageName) - .userState(userId) - .setOverlayPaths(newOverlayPaths); - - for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size(); mapIndex++) { - String libName = libNameToModifiedDependents.keyAt(mapIndex); - ArraySet<String> modifiedDependents = - libNameToModifiedDependents.valueAt(mapIndex); - for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) { - mutator.forPackage(modifiedDependents.valueAt(setIndex)) - .userState(userId) - .setOverlayPathsForLibrary(libName, newOverlayPaths); + for (int i = 0; i < numberOfPendingChanges; i++) { + final String targetPackageName = pendingChanges.keyAt(i); + final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i); + + if (!outUpdatedPackageNames.contains(targetPackageName)) { + continue; + } + + mutator.forPackage(targetPackageName) + .userState(userId) + .setOverlayPaths(newOverlayPaths); + + final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents = + targetPkgToLibNameToModifiedDependents.get( + targetPackageName); + if (libNameToModifiedDependents == null) { + continue; + } + + for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size(); + mapIndex++) { + String libName = libNameToModifiedDependents.keyAt(mapIndex); + ArraySet<String> modifiedDependents = + libNameToModifiedDependents.valueAt(mapIndex); + for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) { + mutator.forPackage(modifiedDependents.valueAt(setIndex)) + .userState(userId) + .setOverlayPathsForLibrary(libName, newOverlayPaths); + } } } }); } invalidatePackageInfoCache(); - - return true; } private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions( diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 16829e0e8f6c..f44d922ea716 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -115,6 +115,6 @@ public final class PackageManagerServiceTestParams { public ResolveIntentHelper resolveIntentHelper; public DexOptHelper dexOptHelper; public SuspendPackageHelper suspendPackageHelper; - public StorageEventHelper storageEventHelper; public DistractingPackageHelper distractingPackageHelper; + public StorageEventHelper storageEventHelper; } diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java index 2016fc3093b3..8302c2b4e76b 100644 --- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java +++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java @@ -167,7 +167,7 @@ final class PackageSessionVerifier { } return new VerificationParams(user, session.stageDir, observer, session.params, session.getInstallSource(), session.getInstallerUid(), session.getSigningDetails(), - session.sessionId, session.getPackageLite(), mPm); + session.sessionId, session.getPackageLite(), session.getUserActionRequired(), mPm); } /** diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 2bae00f91b82..f84db1f7abbe 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -1227,6 +1227,11 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return pkgState.isUpdatedSystemApp(); } + @Override + public boolean isApkInUpdatedApex() { + return pkgState.isApkInUpdatedApex(); + } + public PackageSetting setDomainSetId(@NonNull UUID domainSetId) { mDomainSetId = domainSetId; onChanged(); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index e6d59d43ffbe..9e960c3e027d 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -799,7 +799,6 @@ public final class Settings implements Watchable, Snappable { // always make sure the system package code and resource paths dont change if (dp == null && p.getPkg() != null && p.getPkg().isSystem() && !p.getPkgState().isUpdatedSystemApp()) { - p.getPkgState().setUpdatedSystemApp(true); final PackageSetting disabled; if (replaced) { // a little trick... when we install the new package, we don't @@ -810,6 +809,7 @@ public final class Settings implements Watchable, Snappable { } else { disabled = p; } + p.getPkgState().setUpdatedSystemApp(true); mDisabledSysPackages.put(name, disabled); SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(disabled); if (sharedUserSetting != null) { diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index b3723fb61f5a..f57eaaef25a4 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -894,8 +894,12 @@ class ShortcutPackage extends ShortcutPackageItem { // Get the list of all dynamic shortcuts in this package. final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are + // included in the result findAll(shortcuts, ShortcutInfo::isNonManifestVisible, - ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION); + ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION, + mShortcutUser.mService.mContext.getPackageName(), + 0, /*getPinnedByAnyLauncher=*/ false); final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>(); for (int i = 0; i < shortcuts.size(); i++) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 358e71a70550..896df24393f4 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1873,6 +1873,44 @@ public class UserManagerService extends IUserManager.Stub { } @Override + public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) { + checkCreateUsersPermission("update ephemeral user flag"); + UserData userToUpdate = null; + synchronized (mPackagesLock) { + synchronized (mUsersLock) { + final UserData userData = mUsers.get(userId); + if (userData == null) { + Slog.e(LOG_TAG, "User not found for setting ephemeral mode: u" + userId); + return false; + } + boolean isEphemeralUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL) != 0; + boolean isEphemeralOnCreateUser = + (userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0; + // when user is created in ephemeral mode via FLAG_EPHEMERAL + // its state cannot be changed to non ephemeral. + // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state + if (isEphemeralOnCreateUser && !enableEphemeral) { + Slog.e(LOG_TAG, "Failed to change user state to non-ephemeral for user " + + userId); + return false; + } + if (isEphemeralUser != enableEphemeral) { + if (enableEphemeral) { + userData.info.flags |= UserInfo.FLAG_EPHEMERAL; + } else { + userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL; + } + userToUpdate = userData; + } + } + if (userToUpdate != null) { + writeUserLP(userToUpdate); + } + } + return true; + } + + @Override public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) { try { checkManageUsersPermission("update users"); @@ -3979,6 +4017,10 @@ public class UserManagerService extends IUserManager.Stub { flags &= ~UserInfo.FLAG_EPHEMERAL; } + if ((flags & UserInfo.FLAG_EPHEMERAL) != 0) { + flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE; + } + userInfo = new UserInfo(userId, name, null, flags, userType); userInfo.serialNumber = mNextSerialNumber++; userInfo.creationTime = getCreationTime(); diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java index 7423bf65c6a5..a3335609caf1 100644 --- a/services/core/java/com/android/server/pm/VerificationParams.java +++ b/services/core/java/com/android/server/pm/VerificationParams.java @@ -123,6 +123,7 @@ final class VerificationParams extends HandlerParams { final long mRequiredInstalledVersionCode; final int mDataLoaderType; final int mSessionId; + final boolean mUserActionRequired; private boolean mWaitForVerificationToComplete; private boolean mWaitForIntegrityVerificationToComplete; @@ -135,7 +136,7 @@ final class VerificationParams extends HandlerParams { VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, InstallSource installSource, int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite, - PackageManagerService pm) { + boolean userActionRequired, PackageManagerService pm) { super(user, pm); mOriginInfo = OriginInfo.fromStagedFile(stagedDir); mObserver = observer; @@ -154,6 +155,7 @@ final class VerificationParams extends HandlerParams { ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; mSessionId = sessionId; mPackageLite = lite; + mUserActionRequired = userActionRequired; } @Override @@ -430,6 +432,8 @@ final class VerificationParams extends HandlerParams { verification.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId); + verification.putExtra(PackageManager.EXTRA_USER_ACTION_REQUIRED, mUserActionRequired); + populateInstallerExtras(verification); // Streaming installation timeout schema is enabled only for: diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 849f53026c99..16cf721ffc7e 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1022,7 +1022,8 @@ final class DefaultPermissionGrantPolicy { } for (String packageName : packageNames) { grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId, - PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS, + PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS); + grantPermissionsToPackage(NO_PM_CACHE, packageName, userId, false, false, NOTIFICATION_PERMISSIONS); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 5a05134bed81..5d6ebec6cc30 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -277,10 +277,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean checkAutoRevokeAccess(AndroidPackage pkg, int callingUid) { - if (pkg == null) { - return false; - } - final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; @@ -292,6 +288,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { + Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS + " or be the installer on record"); } + + if (pkg == null || mPackageManagerInt.filterAppAccess(pkg, callingUid, + UserHandle.getUserId(callingUid))) { + return false; + } + return true; } @@ -301,9 +303,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); final int callingUid = Binder.getCallingUid(); - if (mPackageManagerInt.filterAppAccess(packageName, callingUid, userId)) { - return false; - } if (!checkAutoRevokeAccess(pkg, callingUid)) { return false; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index d11ea532f140..d21eabe5fe80 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -47,7 +47,6 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED; -import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS; @@ -3251,9 +3250,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } // Only enforce the allowlist on boot if (!mSystemReady) { - final boolean isInUpdatedApex = containingApexPackageName != null - && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName, - MATCH_ACTIVE_PACKAGE)); + final boolean isInUpdatedApex = packageSetting.isApkInUpdatedApex(); // Apps that are in updated apexs' do not need to be allowlisted if (!isInUpdatedApex) { Slog.w(TAG, "Privileged permission " + permissionName + " for package " diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java index 7726d7fc226b..b5e0e4416fe4 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageState.java +++ b/services/core/java/com/android/server/pm/pkg/PackageState.java @@ -303,6 +303,11 @@ public interface PackageState { boolean isUpdatedSystemApp(); /** + * Whether this app is packaged in an updated apex. + */ + boolean isApkInUpdatedApex(); + + /** * @see AndroidPackageApi#isVendor() */ boolean isVendor(); diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java index 3170304985b5..878a837585ca 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java +++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java @@ -71,6 +71,7 @@ public class PackageStateImpl implements PackageState { INSTALL_PERMISSIONS_FIXED, UPDATE_AVAILABLE, UPDATED_SYSTEM_APP, + APK_IN_UPDATED_APEX, }) public @interface Flags { } @@ -89,6 +90,7 @@ public class PackageStateImpl implements PackageState { private static final int INSTALL_PERMISSIONS_FIXED = 1 << 11; private static final int UPDATE_AVAILABLE = 1 << 12; private static final int UPDATED_SYSTEM_APP = 1 << 13; + private static final int APK_IN_UPDATED_APEX = 1 << 14; } private int mBooleans; @@ -187,6 +189,7 @@ public class PackageStateImpl implements PackageState { setBoolean(Booleans.UPDATE_AVAILABLE, pkgState.isUpdateAvailable()); mLastPackageUsageTime = pkgState.getLastPackageUsageTime(); setBoolean(Booleans.UPDATED_SYSTEM_APP, pkgState.isUpdatedSystemApp()); + setBoolean(Booleans.APK_IN_UPDATED_APEX, pkgState.isApkInUpdatedApex()); mSigningInfo = pkgState.getSigningInfo(); SparseArray<? extends PackageUserState> userStates = pkgState.getUserStates(); @@ -264,6 +267,11 @@ public class PackageStateImpl implements PackageState { } @Override + public boolean isApkInUpdatedApex() { + return getBoolean(Booleans.APK_IN_UPDATED_APEX); + } + + @Override public boolean isVendor() { return getBoolean(Booleans.VENDOR); } diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java index 7bd720acc799..fad2f857f656 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java +++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java @@ -52,6 +52,8 @@ public class PackageStateUnserialized { private List<String> usesLibraryFiles = emptyList(); private boolean updatedSystemApp; + private boolean apkInApex; + private boolean apkInUpdatedApex; @NonNull private volatile long[] lastPackageUsageTimeInMills; @@ -116,6 +118,8 @@ public class PackageStateUnserialized { } this.updatedSystemApp = other.updatedSystemApp; + this.apkInApex = other.apkInApex; + this.apkInUpdatedApex = other.apkInUpdatedApex; this.lastPackageUsageTimeInMills = other.lastPackageUsageTimeInMills; this.overrideSeInfo = other.overrideSeInfo; mPackageSetting.onChanged(); @@ -150,6 +154,18 @@ public class PackageStateUnserialized { return this; } + public PackageStateUnserialized setApkInApex(boolean value) { + apkInApex = value; + mPackageSetting.onChanged(); + return this; + } + + public PackageStateUnserialized setApkInUpdatedApex(boolean value) { + apkInUpdatedApex = value; + mPackageSetting.onChanged(); + return this; + } + public PackageStateUnserialized setLastPackageUsageTimeInMills(@NonNull long... value) { lastPackageUsageTimeInMills = value; mPackageSetting.onChanged(); @@ -198,6 +214,16 @@ public class PackageStateUnserialized { } @DataClass.Generated.Member + public boolean isApkInApex() { + return apkInApex; + } + + @DataClass.Generated.Member + public boolean isApkInUpdatedApex() { + return apkInUpdatedApex; + } + + @DataClass.Generated.Member public @NonNull long[] getLastPackageUsageTimeInMills() { long[] _lastPackageUsageTimeInMills = lastPackageUsageTimeInMills; if (_lastPackageUsageTimeInMills == null) { @@ -222,10 +248,10 @@ public class PackageStateUnserialized { } @DataClass.Generated( - time = 1642554781099L, + time = 1646203523807L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java", - inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\npublic void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)") + inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate boolean apkInApex\nprivate boolean apkInUpdatedApex\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate final @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\npublic void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInUpdatedApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java index 91d201096fcd..8a64884ad6a5 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java @@ -321,19 +321,15 @@ public class PackageInfoWithoutStateUtils { pi.applicationInfo.sourceDir = apexFile.getPath(); pi.applicationInfo.publicSourceDir = apexFile.getPath(); + pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; if (apexInfo.isFactory) { - pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } else { - pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; pi.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } - if (apexInfo.isActive) { - pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; - } else { - pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED; - } pi.isApex = true; + pi.isActiveApex = apexInfo.isActive; } final SigningDetails signingDetails = pkg.getSigningDetails(); diff --git a/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING index ba4a62cdbbf1..8a1982a339ea 100644 --- a/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING @@ -12,9 +12,6 @@ "name": "CtsDomainVerificationDeviceStandaloneTestCases" }, { - "name": "CtsDomainVerificationDeviceMultiUserTestCases" - }, - { "name": "CtsDomainVerificationHostTestCases" } ] diff --git a/services/core/java/com/android/server/power/ShutdownCheckPoints.java b/services/core/java/com/android/server/power/ShutdownCheckPoints.java index 05ee7dfb4cae..1a9eae6b6d8d 100644 --- a/services/core/java/com/android/server/power/ShutdownCheckPoints.java +++ b/services/core/java/com/android/server/power/ShutdownCheckPoints.java @@ -36,7 +36,6 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.LinkedList; import java.util.List; /** @@ -56,7 +55,7 @@ public final class ShutdownCheckPoints { private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z"); - private final LinkedList<CheckPoint> mCheckPoints; + private final ArrayList<CheckPoint> mCheckPoints; private final Injector mInjector; private ShutdownCheckPoints() { @@ -85,7 +84,7 @@ public final class ShutdownCheckPoints { @VisibleForTesting ShutdownCheckPoints(Injector injector) { - mCheckPoints = new LinkedList<>(); + mCheckPoints = new ArrayList<>(); mInjector = injector; } @@ -144,8 +143,8 @@ public final class ShutdownCheckPoints { private void recordCheckPointInternal(CheckPoint checkPoint) { synchronized (mCheckPoints) { - mCheckPoints.addLast(checkPoint); - if (mCheckPoints.size() > mInjector.maxCheckPoints()) mCheckPoints.removeFirst(); + mCheckPoints.add(checkPoint); + if (mCheckPoints.size() > mInjector.maxCheckPoints()) mCheckPoints.remove(0); } } diff --git a/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java index 3543e9319a88..928d128d6d16 100644 --- a/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java +++ b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java @@ -32,6 +32,7 @@ import com.android.internal.org.bouncycastle.asn1.ASN1Set; import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject; import com.android.internal.org.bouncycastle.asn1.x509.Certificate; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; @@ -147,7 +148,7 @@ class AndroidKeystoreAttestationVerificationAttributes { @NonNull static AndroidKeystoreAttestationVerificationAttributes fromCertificate( @NonNull X509Certificate x509Certificate) - throws Exception { + throws CertificateEncodingException, IOException { return new AndroidKeystoreAttestationVerificationAttributes(x509Certificate); } @@ -281,7 +282,7 @@ class AndroidKeystoreAttestationVerificationAttributes { } private AndroidKeystoreAttestationVerificationAttributes(X509Certificate x509Certificate) - throws Exception { + throws CertificateEncodingException, IOException { Certificate certificate = Certificate.getInstance( new ASN1InputStream(x509Certificate.getEncoded()).readObject()); ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions() @@ -380,7 +381,7 @@ class AndroidKeystoreAttestationVerificationAttributes { } private void parseAttestationApplicationId(byte [] attestationApplicationId) - throws Exception { + throws IOException { ASN1Sequence outerSequence = ASN1Sequence.getInstance( new ASN1InputStream(attestationApplicationId).readObject()); Map<String, Long> packageNameVersion = new HashMap<>(); diff --git a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java index 0f8be5a77944..bcc39ba1bde1 100644 --- a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java +++ b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java @@ -22,8 +22,10 @@ import static android.security.attestationverification.AttestationVerificationMa import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS; import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE; import static android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY; +import static android.security.attestationverification.AttestationVerificationManager.localBindingTypeToString; import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED; +import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.fromCertificate; import static java.nio.charset.StandardCharsets.UTF_8; @@ -31,6 +33,7 @@ import android.annotation.NonNull; import android.content.Context; import android.os.Build; import android.os.Bundle; +import android.security.attestationverification.AttestationVerificationManager.LocalBindingType; import android.util.Log; import android.util.Slog; @@ -40,8 +43,10 @@ import com.android.internal.annotations.VisibleForTesting; import org.json.JSONObject; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.security.InvalidAlgorithmParameterException; import java.security.cert.CertPath; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; @@ -65,17 +70,26 @@ import java.util.Objects; import java.util.Set; /** - * Verifies Android key attestation according to the {@code PROFILE_PEER_DEVICE} profile. + * Verifies Android key attestation according to the + * {@link android.security.attestationverification.AttestationVerificationManager#PROFILE_PEER_DEVICE PROFILE_PEER_DEVICE} + * profile. * - * Trust anchors are vendor-defined via the vendor_required_attestation_certificates.xml resource. + * <p> * The profile is satisfied by checking all the following: - * * TrustAnchor match - * * Certificate validity - * * Android OS 10 or higher - * * Hardware backed key store - * * Verified boot locked - * * Remote Patch level must be within 1 year of local patch if local patch is less than 1 year old. + * <ul> + * <li> TrustAnchor match + * <li> Certificate validity + * <li> Android OS 10 or higher + * <li> Hardware backed key store + * <li> Verified boot locked + * <li> Remote Patch level must be within 1 year of local patch if local patch is less than 1 year + * old. + * </ul> * + * <p> + * Trust anchors are vendor-defined by populating + * {@link R.array#vendor_required_attestation_certificates} string array (defenined in + * {@code frameworks/base/core/res/res/values/vendor_required_attestation_certificates.xml}). */ class AttestationVerificationPeerDeviceVerifier { private static final String TAG = "AVF"; @@ -87,20 +101,8 @@ class AttestationVerificationPeerDeviceVerifier { private final boolean mRevocationEnabled; private final LocalDate mTestSystemDate; private final LocalDate mTestLocalPatchDate; - private CertificateFactory mCertificateFactory; - private CertPathValidator mCertPathValidator; - - private static void debugVerboseLog(String str, Throwable t) { - if (DEBUG) { - Slog.v(TAG, str, t); - } - } - - private static void debugVerboseLog(String str) { - if (DEBUG) { - Slog.v(TAG, str); - } - } + private final CertificateFactory mCertificateFactory; + private final CertPathValidator mCertPathValidator; AttestationVerificationPeerDeviceVerifier(@NonNull Context context) throws Exception { mContext = Objects.requireNonNull(context); @@ -128,53 +130,71 @@ class AttestationVerificationPeerDeviceVerifier { /** * Verifies attestation for public key or challenge local binding. - * + * <p> * The attestations must be suitable for {@link java.security.cert.CertificateFactory} * The certificates in the attestation provided must be DER-encoded and may be supplied in * binary or printable (Base64) encoding. If the certificate is provided in Base64 encoding, - * it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and must be bounded at - * the end by -----END CERTIFICATE-----. + * it must be bounded at the beginning by {@code -----BEGIN CERTIFICATE-----}, and must be + * bounded at the end by {@code -----END CERTIFICATE-----}. * * @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported. * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported. * @param attestation Certificates should be DER encoded with leaf certificate appended first. */ int verifyAttestation( - int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) { - int status = RESULT_FAILURE; - + @LocalBindingType int localBindingType, + @NonNull Bundle requirements, + @NonNull byte[] attestation) { if (mCertificateFactory == null) { - debugVerboseLog("Was unable to initialize CertificateFactory onCreate."); - return status; + debugVerboseLog("Unable to access CertificateFactory"); + return RESULT_FAILURE; } if (mCertPathValidator == null) { - debugVerboseLog("Was unable to initialize CertPathValidator onCreate."); - return status; + debugVerboseLog("Unable to access CertPathValidator"); + return RESULT_FAILURE; } - List<X509Certificate> certificates; - try { - certificates = getCertificates(attestation); - } catch (CertificateException e) { - debugVerboseLog("Unable to parse attestation certificates.", e); - return status; + // Check if the provided local binding type is supported and if the provided requirements + // "match" the binding type. + if (!validateAttestationParameters(localBindingType, requirements)) { + return RESULT_FAILURE; } - if (certificates.isEmpty()) { - debugVerboseLog("Attestation contains no certificates."); - return status; - } + try { + // First: parse and validate the certificate chain. + final List<X509Certificate> certificateChain = getCertificates(attestation); + // (returns void, but throws CertificateException and other similar Exceptions) + validateCertificateChain(certificateChain); - X509Certificate leafNode = certificates.get(0); - if (validateRequirements(localBindingType, requirements) - && validateCertificateChain(certificates) - && checkCertificateAttributes(leafNode, localBindingType, requirements)) { - status = RESULT_SUCCESS; - } else { - status = RESULT_FAILURE; + final var leafCertificate = certificateChain.get(0); + final var attestationExtension = fromCertificate(leafCertificate); + + // Second: verify if the attestation satisfies the "peer device" porfile. + if (!checkAttestationForPeerDeviceProfile(attestationExtension)) { + return RESULT_FAILURE; + } + + // Third: check if the attestation satisfies local binding requirements. + if (!checkLocalBindingRequirements( + leafCertificate, attestationExtension, localBindingType, requirements)) { + return RESULT_FAILURE; + } + + return RESULT_SUCCESS; + } catch (CertificateException | CertPathValidatorException + | InvalidAlgorithmParameterException | IOException e) { + // Catch all non-RuntimeExpceptions (all of these are thrown by either getCertificates() + // or validateCertificateChain() or + // AndroidKeystoreAttestationVerificationAttributes.fromCertificate()) + debugVerboseLog("Unable to parse/validate Android Attestation certificate(s)", e); + return RESULT_FAILURE; + } catch (RuntimeException e) { + // Catch everyting else (RuntimeExpcetions), since we don't want to throw any exceptions + // out of this class/method. + debugVerboseLog("Unexpected error", e); + return RESULT_FAILURE; } - return status; } @NonNull @@ -189,14 +209,19 @@ class AttestationVerificationPeerDeviceVerifier { return certificates; } - private boolean validateRequirements(int localBindingType, Bundle requirements) { - if (requirements.size() != 1) { - debugVerboseLog("Requirements does not contain exactly 1 key."); + /** + * Check if the {@code localBindingType} is supported and if the {@code requirements} contains + * the required parameter for the given {@code @LocalBindingType}. + */ + private boolean validateAttestationParameters( + @LocalBindingType int localBindingType, @NonNull Bundle requirements) { + if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) { + debugVerboseLog("Binding type is not supported: " + localBindingType); return false; } - if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) { - debugVerboseLog("Binding type is not supported: " + localBindingType); + if (requirements.size() != 1) { + debugVerboseLog("Requirements does not contain exactly 1 key."); return false; } @@ -213,29 +238,25 @@ class AttestationVerificationPeerDeviceVerifier { return true; } - private boolean validateCertificateChain(List<X509Certificate> certificates) { + private void validateCertificateChain(List<X509Certificate> certificates) + throws CertificateException, CertPathValidatorException, + InvalidAlgorithmParameterException { if (certificates.size() < 2) { debugVerboseLog("Certificate chain less than 2 in size."); - return false; + throw new CertificateException("Certificate chain less than 2 in size."); } - try { - CertPath certificatePath = mCertificateFactory.generateCertPath(certificates); - PKIXParameters validationParams = new PKIXParameters(mTrustAnchors); - if (mRevocationEnabled) { - // Checks Revocation Status List based on - // https://developer.android.com/training/articles/security-key-attestation#certificate_status - PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker(); - validationParams.addCertPathChecker(checker); - } - // Do not use built-in revocation status checker. - validationParams.setRevocationEnabled(false); - mCertPathValidator.validate(certificatePath, validationParams); - } catch (Throwable t) { - debugVerboseLog("Invalid certificate chain.", t); - return false; + CertPath certificatePath = mCertificateFactory.generateCertPath(certificates); + PKIXParameters validationParams = new PKIXParameters(mTrustAnchors); + if (mRevocationEnabled) { + // Checks Revocation Status List based on + // https://developer.android.com/training/articles/security-key-attestation#certificate_status + PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker(); + validationParams.addCertPathChecker(checker); } - return true; + // Do not use built-in revocation status checker. + validationParams.setRevocationEnabled(false); + mCertPathValidator.validate(certificatePath, validationParams); } private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException { @@ -267,18 +288,44 @@ class AttestationVerificationPeerDeviceVerifier { R.array.vendor_required_attestation_certificates); } - private boolean checkCertificateAttributes( - X509Certificate leafCertificate, int localBindingType, Bundle requirements) { - AndroidKeystoreAttestationVerificationAttributes attestationAttributes; - try { - attestationAttributes = - AndroidKeystoreAttestationVerificationAttributes.fromCertificate( - leafCertificate); - } catch (Throwable t) { - debugVerboseLog("Could not get ParsedAttestationAttributes from Certificate.", t); - return false; + private boolean checkLocalBindingRequirements( + @NonNull X509Certificate leafCertificate, + @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, + @LocalBindingType int localBindingType, + @NonNull Bundle requirements) { + switch (localBindingType) { + case TYPE_PUBLIC_KEY: + // Verify leaf public key matches provided public key. + final boolean publicKeyMatches = checkPublicKey( + leafCertificate, requirements.getByteArray(PARAM_PUBLIC_KEY)); + if (!publicKeyMatches) { + debugVerboseLog( + "Provided public key does not match leaf certificate public key."); + return false; + } + break; + + case TYPE_CHALLENGE: + // Verify challenge matches provided challenge. + final boolean attestationChallengeMatches = checkAttestationChallenge( + attestationAttributes, requirements.getByteArray(PARAM_CHALLENGE)); + if (!attestationChallengeMatches) { + debugVerboseLog( + "Provided challenge does not match leaf certificate challenge."); + return false; + } + break; + + default: + throw new IllegalArgumentException("Unsupported local binding type " + + localBindingTypeToString(localBindingType)); } + return true; + } + + private boolean checkAttestationForPeerDeviceProfile( + @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes) { // Checks for support of Keymaster 4. if (attestationAttributes.getAttestationVersion() < 3) { debugVerboseLog("Attestation version is not at least 3 (Keymaster 4)."); @@ -344,23 +391,20 @@ class AttestationVerificationPeerDeviceVerifier { return false; } - // Verify leaf public key matches provided public key. - if (localBindingType == TYPE_PUBLIC_KEY - && !Arrays.equals(requirements.getByteArray(PARAM_PUBLIC_KEY), - leafCertificate.getPublicKey().getEncoded())) { - debugVerboseLog("Provided public key does not match leaf certificate public key."); - return false; - } + return true; + } - // Verify challenge matches provided challenge. - if (localBindingType == TYPE_CHALLENGE - && !Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), - attestationAttributes.getAttestationChallenge().toByteArray())) { - debugVerboseLog("Provided challenge does not match leaf certificate challenge."); - return false; - } + private boolean checkPublicKey( + @NonNull Certificate certificate, @NonNull byte[] expectedPublicKey) { + final byte[] publicKey = certificate.getPublicKey().getEncoded(); + return Arrays.equals(publicKey, expectedPublicKey); + } - return true; + private boolean checkAttestationChallenge( + @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, + @NonNull byte[] expectedChallenge) { + final byte[] challenge = attestationAttributes.getAttestationChallenge().toByteArray(); + return Arrays.equals(challenge, expectedChallenge); } /** @@ -507,4 +551,16 @@ class AttestationVerificationPeerDeviceVerifier { R.string.vendor_required_attestation_revocation_list_url); } } + + private static void debugVerboseLog(String str, Throwable t) { + if (DEBUG) { + Slog.v(TAG, str, t); + } + } + + private static void debugVerboseLog(String str) { + if (DEBUG) { + Slog.v(TAG, str); + } + } } diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index e3dcfd0c89c0..881bdbd3bc6a 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -65,7 +65,6 @@ import com.android.internal.app.AssistUtils; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -226,12 +225,18 @@ public class SliceManagerService extends ISliceManager.Stub { } @Override - public int checkSlicePermission(Uri uri, String callingPkg, String pkg, int pid, int uid, + public int checkSlicePermission(Uri uri, String callingPkg, int pid, int uid, String[] autoGrantPermissions) { + return checkSlicePermissionInternal(uri, callingPkg, null /* pkg */, pid, uid, + autoGrantPermissions); + } + + private int checkSlicePermissionInternal(Uri uri, String callingPkg, String pkg, int pid, + int uid, String[] autoGrantPermissions) { int userId = UserHandle.getUserId(uid); if (pkg == null) { for (String p : mContext.getPackageManager().getPackagesForUid(uid)) { - if (checkSlicePermission(uri, callingPkg, p, pid, uid, autoGrantPermissions) + if (checkSlicePermissionInternal(uri, callingPkg, p, pid, uid, autoGrantPermissions) == PERMISSION_GRANTED) { return PERMISSION_GRANTED; } @@ -395,7 +400,8 @@ public class SliceManagerService extends ISliceManager.Stub { } protected int checkAccess(String pkg, Uri uri, int uid, int pid) { - return checkSlicePermission(uri, null, pkg, pid, uid, null); + return checkSlicePermissionInternal(uri, null /* callingPkg */, pkg, pid, uid, + null /* autoGrantPermissions */); } private String getProviderPkg(Uri uri, int user) { @@ -404,7 +410,7 @@ public class SliceManagerService extends ISliceManager.Stub { String providerName = getUriWithoutUserId(uri).getAuthority(); ProviderInfo provider = mContext.getPackageManager().resolveContentProviderAsUser( providerName, 0, getUserIdFromUri(uri, user)); - return provider.packageName; + return provider == null ? null : provider.packageName; } finally { Binder.restoreCallingIdentity(ident); } @@ -629,7 +635,7 @@ public class SliceManagerService extends ISliceManager.Stub { .scheme(ContentResolver.SCHEME_CONTENT) .authority(authority) .build(), 0); - return mPermissions.getAllPackagesGranted(pkg); + return pkg == null ? new String[0] : mPermissions.getAllPackagesGranted(pkg); } /** diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index 07725038255e..b6ab35169fc4 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -155,8 +155,6 @@ final class TvInputHal implements Handler.Callback { // Handler.Callback implementation - private final Queue<Message> mPendingMessageQueue = new LinkedList<>(); - @Override public boolean handleMessage(Message msg) { switch (msg.what) { diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index f57a852fe8c5..98dfb009e4ef 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -69,7 +69,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -89,7 +88,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { private final TvInputHal mHal = new TvInputHal(this); private final SparseArray<Connection> mConnections = new SparseArray<>(); private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>(); - private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>(); + private final List<HdmiDeviceInfo> mHdmiDeviceList = new ArrayList<>(); /* A map from a device ID to the matching TV input ID. */ private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>(); /* A map from a HDMI logical address to the matching TV input ID. */ @@ -112,9 +111,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { private int mCurrentMaxIndex = 0; private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); - private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>(); + private final List<Message> mPendingHdmiDeviceEvents = new ArrayList<>(); - private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>(); + private final List<Message> mPendingTvinputInfoEvents = new ArrayList<>(); // Calls to mListener should happen here. private final Handler mHandler = new ListenerHandler(); @@ -234,11 +233,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } else { Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED, deviceId, cableConnectionStatus, connection); - for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) { - if (it.next().arg1 == deviceId) { - it.remove(); - } - } + mPendingTvinputInfoEvents.removeIf(message -> message.arg1 == deviceId); mPendingTvinputInfoEvents.add(msg); } ITvInputHardwareCallback callback = connection.getCallbackLocked(); diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 7f84f61a91ff..3b8677d70be2 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -68,6 +68,7 @@ import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT; import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME; @@ -102,6 +103,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.util.LatencyTracker; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -771,6 +773,12 @@ class ActivityMetricsLogger { info.mReason = activityToReason.valueAt(index); info.mLoggedTransitionStarting = true; if (info.mIsDrawn) { + if (info.mReason == APP_TRANSITION_RECENTS_ANIM) { + final LatencyTracker latencyTracker = r.mWmService.mLatencyTracker; + final int duration = info.mSourceEventDelayMs + info.mCurrentTransitionDelayMs; + mLoggerHandler.post(() -> latencyTracker.logAction( + LatencyTracker.ACTION_START_RECENTS_ANIMATION, duration)); + } done(false /* abort */, info, "notifyTransitionStarting drawn", timestampNs); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c5251296b91e..dc4e1174edf3 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6300,7 +6300,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // starting window is drawn, the transition can start earlier. Exclude finishing and bubble // because it may be a trampoline. if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble - && !mDisplayContent.mAppTransition.isReady() + && mVisibleRequested && !mDisplayContent.mAppTransition.isReady() && !mDisplayContent.mAppTransition.isRunning() && mDisplayContent.isNextTransitionForward()) { // The pending transition state will be cleared after the transition is started, so diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index cefc8717ed01..6f13f86c5bdf 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -96,8 +96,8 @@ import com.android.internal.protolog.common.ProtoLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.LinkedList; import java.util.function.Consumer; import java.util.function.Predicate; @@ -854,7 +854,7 @@ public class AppTransitionController { boolean visible) { // The candidates of animation targets, which might be able to promote to higher level. - final LinkedList<WindowContainer> candidates = new LinkedList<>(); + final ArrayDeque<WindowContainer> candidates = new ArrayDeque<>(); final ArraySet<ActivityRecord> apps = visible ? openingApps : closingApps; for (int i = 0; i < apps.size(); ++i) { final ActivityRecord app = apps.valueAt(i); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 9bf988f16090..7a2d4f955beb 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -54,10 +54,10 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; import java.util.function.Consumer; @@ -319,13 +319,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final WindowManagerGlobalLock mGlobalLock; // List of task organizers by priority - private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>(); + private final ArrayDeque<ITaskOrganizer> mTaskOrganizers = new ArrayDeque<>(); private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); // Pending task events due to layout deferred. private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>(); // Set of organized tasks (by taskId) that dispatch back pressed to their organizers - private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); + private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet<>(); private RunningTaskInfo mTmpTaskInfo; private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 0bdc00b8b74d..b69771c38542 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -52,6 +52,7 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; @@ -670,8 +671,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe handleNonAppWindowsInTransition(dc, mType, mFlags); - reportStartReasonsToLogger(); - // The callback is only populated for custom activity-level client animations sendRemoteCallback(mClientAnimationStartCallback); @@ -763,6 +762,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } mSyncId = -1; mOverrideOptions = null; + + reportStartReasonsToLogger(); } /** @@ -975,11 +976,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = mParticipants.size() - 1; i >= 0; --i) { ActivityRecord r = mParticipants.valueAt(i).asActivityRecord(); if (r == null || !r.mVisibleRequested) continue; + int transitionReason = APP_TRANSITION_WINDOWS_DRAWN; // At this point, r is "ready", but if it's not "ALL ready" then it is probably only // ready due to starting-window. - reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData - && !r.mLastAllReadyAtSync) - ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN); + if (r.mStartingData instanceof SplashScreenStartingData && !r.mLastAllReadyAtSync) { + transitionReason = APP_TRANSITION_SPLASH_SCREEN; + } else if (r.isActivityTypeHomeOrRecents() && isTransientLaunch(r)) { + transitionReason = APP_TRANSITION_RECENTS_ANIM; + } + reasons.put(r, transitionReason); } mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting( reasons); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 4dbcea1e4751..70c3ab73d438 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2998,7 +2998,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder(); - if (isTaskTransitOld(transit)) { + if (isTaskTransitOld(transit) && getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor()); // TODO: Remove when we migrate to shell (b/202383002) if (mWmService.mTaskTransitionSpec != null) { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 0d49f5fffb4b..cfcbc3ac0676 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -185,6 +185,7 @@ cc_defaults { "android.system.suspend.control-V1-cpp", "android.system.suspend.control.internal-cpp", "android.system.suspend-V1-ndk", + "server_configurable_flags", "service.incremental", ], diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 3c5ebe7c62ee..ffda8be56b3f 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -50,6 +50,7 @@ #include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> +#include <server_configurable_flags/get_flags.h> #include <ui/Region.h> #include <utils/Log.h> #include <utils/Looper.h> @@ -87,6 +88,13 @@ namespace android { // where the speed ranges from -7 to + 7 and is supplied by the user. static const float POINTER_SPEED_EXPONENT = 1.0f / 4; +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +/** + * Feature flag name. This flag determines which VelocityTracker strategy is used by default. + */ +static const char* VELOCITYTRACKER_STRATEGY = "velocitytracker_strategy"; + static struct { jclass clazz; jmethodID notifyConfigurationChanged; @@ -2120,8 +2128,10 @@ static void nativeReloadDeviceAliases(JNIEnv* env, jobject nativeImplObj) { static std::string dumpInputProperties() { std::string out = "Input properties:\n"; const std::string strategy = - sysprop::InputProperties::velocitytracker_strategy().value_or("default"); - out += " persist.input.velocitytracker.strategy = " + strategy + "\n"; + server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT, + VELOCITYTRACKER_STRATEGY, + "default"); + out += " velocitytracker_strategy (flag value) = " + strategy + "\n"; out += "\n"; return out; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 18bffebb427c..b865f76c0823 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -11732,6 +11732,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ap = getParentOfAdminIfRequired(getOrganizationOwnedProfileOwnerLocked(caller), parent); } else { + Preconditions.checkCallAuthorization(!isFinancedDeviceOwner(caller)); ap = getParentOfAdminIfRequired(getProfileOwnerOrDeviceOwnerLocked(caller), parent); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 2b431b6c3ea5..2f8d10c61f8f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -79,7 +79,6 @@ import android.os.storage.IStorageManager; import android.provider.DeviceConfig; import android.provider.Settings; import android.server.ServerProtoEnums; -import android.sysprop.VoldProperties; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; @@ -238,9 +237,6 @@ public final class SystemServer implements Dumpable { private static final String TAG = "SystemServer"; - private static final String ENCRYPTING_STATE = "trigger_restart_min_framework"; - private static final String ENCRYPTED_STATE = "1"; - private static final long SLOW_DISPATCH_THRESHOLD_MS = 100; private static final long SLOW_DELIVERY_THRESHOLD_MS = 200; @@ -462,7 +458,6 @@ public final class SystemServer implements Dumpable { private DataLoaderManagerService mDataLoaderManagerService; private long mIncrementalServiceHandle = 0; - private boolean mOnlyCore; private boolean mFirstBoot; private final int mStartCount; private final boolean mRuntimeRestart; @@ -1203,16 +1198,6 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); t.traceEnd(); - // Only run "core" apps if we're encrypting the device. - String cryptState = VoldProperties.decrypt().orElse(""); - if (ENCRYPTING_STATE.equals(cryptState)) { - Slog.w(TAG, "Detected encryption in progress - only parsing core apps"); - mOnlyCore = true; - } else if (ENCRYPTED_STATE.equals(cryptState)) { - Slog.w(TAG, "Device encrypted - only parsing core apps"); - mOnlyCore = true; - } - // Start the package manager. if (!mRuntimeRestart) { FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, @@ -1233,7 +1218,7 @@ public final class SystemServer implements Dumpable { Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain"); Pair<PackageManagerService, IPackageManager> pmsPair = PackageManagerService.main( mSystemContext, installer, domainVerificationService, - mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); + mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, /* onlyCore= */ false); mPackageManagerService = pmsPair.first; iPackageManager = pmsPair.second; } finally { @@ -1256,21 +1241,17 @@ public final class SystemServer implements Dumpable { } // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename // A/B artifacts after boot, before anything else might touch/need them. - // Note: this isn't needed during decryption (we don't have /data anyways). - if (!mOnlyCore) { - boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt", - false); - if (!disableOtaDexopt) { - t.traceBegin("StartOtaDexOptService"); - try { - Watchdog.getInstance().pauseWatchingCurrentThread("moveab"); - OtaDexoptService.main(mSystemContext, mPackageManagerService); - } catch (Throwable e) { - reportWtf("starting OtaDexOptService", e); - } finally { - Watchdog.getInstance().resumeWatchingCurrentThread("moveab"); - t.traceEnd(); - } + boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt", false); + if (!disableOtaDexopt) { + t.traceBegin("StartOtaDexOptService"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("moveab"); + OtaDexoptService.main(mSystemContext, mPackageManagerService); + } catch (Throwable e) { + reportWtf("starting OtaDexOptService", e); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("moveab"); + t.traceEnd(); } } @@ -1583,8 +1564,9 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartWindowManagerService"); // WMS needs sensor service ready mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE); - wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, - new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); + wm = WindowManagerService.main(context, inputManager, !mFirstBoot, + /* onlyCore= */ false, new PhoneWindowManager(), + mActivityManagerService.mActivityTaskManager); ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); ServiceManager.addService(Context.INPUT_SERVICE, inputManager, @@ -1757,19 +1739,16 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - - if (!mOnlyCore) { - t.traceBegin("UpdatePackagesIfNeeded"); - try { - Watchdog.getInstance().pauseWatchingCurrentThread("dexopt"); - mPackageManagerService.updatePackagesIfNeeded(); - } catch (Throwable e) { - reportWtf("update packages", e); - } finally { - Watchdog.getInstance().resumeWatchingCurrentThread("dexopt"); - } - t.traceEnd(); + t.traceBegin("UpdatePackagesIfNeeded"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("dexopt"); + mPackageManagerService.updatePackagesIfNeeded(); + } catch (Throwable e) { + reportWtf("update packages", e); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("dexopt"); } + t.traceEnd(); t.traceBegin("PerformFstrimIfNeeded"); try { @@ -2308,13 +2287,9 @@ public final class SystemServer implements Dumpable { t.traceEnd(); // timezone.RulesManagerService will prevent a device starting up if the chain of trust - // required for safe time zone updates might be broken. RuleManagerService cannot do - // this check when mOnlyCore == true, so we don't enable the service in this case. - // This service requires that JobSchedulerService is already started when it starts. - final boolean startRulesManagerService = - !mOnlyCore && context.getResources().getBoolean( - R.bool.config_enableUpdateableTimeZoneRules); - if (startRulesManagerService) { + // required for safe time zone updates might be broken. This service requires that + // JobSchedulerService is already started when it starts. + if (context.getResources().getBoolean(R.bool.config_enableUpdateableTimeZoneRules)) { t.traceBegin("StartTimeZoneRulesManagerService"); mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS); t.traceEnd(); @@ -2720,7 +2695,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("MakeDisplayManagerServiceReady"); try { // TODO: use boot phase and communicate these flags some other way - mDisplayManagerService.systemReady(safeMode, mOnlyCore); + mDisplayManagerService.systemReady(safeMode, /* onlyCore= */ false); } catch (Throwable e) { reportWtf("making Display Manager Service ready", e); } @@ -2828,7 +2803,7 @@ public final class SystemServer implements Dumpable { // be completed before allowing 3rd party final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; Future<?> webviewPrep = null; - if (!mOnlyCore && mWebViewUpdateService != null) { + if (mWebViewUpdateService != null) { webviewPrep = SystemServerInitThreadPool.submit(() -> { Slog.i(TAG, WEBVIEW_PREPARATION); TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java new file mode 100644 index 000000000000..3f6199c459ce --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.test.appenumeration; + +import static org.junit.Assert.assertThrows; + +import android.app.AppGlobals; +import android.app.Instrumentation; +import android.content.pm.IPackageManager; +import android.os.UserHandle; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CrossUserPackageVisibilityTests { + + private Instrumentation mInstrumentation; + private IPackageManager mIPackageManager; + + @Before + public void setup() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mIPackageManager = AppGlobals.getPackageManager(); + } + + @Test + public void testGetSplashScreenTheme_withCrossUserId() { + final int crossUserId = UserHandle.myUserId() + 1; + assertThrows(SecurityException.class, + () -> mIPackageManager.getSplashScreenTheme( + mInstrumentation.getContext().getPackageName(), crossUserId)); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java index a2e813aed720..82334f29099d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java @@ -59,6 +59,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; import org.junit.After; import org.junit.Before; @@ -136,14 +137,17 @@ public class AppOpsServiceTest { .spyStatic(Settings.Global.class) .startMocking(); - // Mock LocalServices.getService(PackageManagerInternal.class).getPackage dependency - // needed by AppOpsService + // Mock LocalServices.getService(PackageManagerInternal.class).getPackageStateInternal + // and getPackage dependency needed by AppOpsService PackageManagerInternal mockPackageManagerInternal = mock(PackageManagerInternal.class); + PackageStateInternal mockMyPSInternal = mock(PackageStateInternal.class); AndroidPackage mockMyPkg = mock(AndroidPackage.class); when(mockMyPkg.isPrivileged()).thenReturn(false); when(mockMyPkg.getUid()).thenReturn(mMyUid); when(mockMyPkg.getAttributions()).thenReturn(Collections.emptyList()); + when(mockPackageManagerInternal.getPackageStateInternal(sMyPackageName)) + .thenReturn(mockMyPSInternal); when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg); doReturn(mockPackageManagerInternal).when( () -> LocalServices.getService(PackageManagerInternal.class)); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 617321beadd2..7755552bcad2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -558,6 +558,40 @@ public class LocalDisplayAdapterTest { } @Test + public void testAfterDisplayStateChanges_committedSetAfterState() throws Exception { + FakeDisplay display = new FakeDisplay(PORT_A); + setUpDisplay(display); + updateAvailableDisplays(); + mAdapter.registerLocked(); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + DisplayDevice displayDevice = mListener.addedDisplays.get(0); + + // Turn off. + Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_OFF, 0, + 0); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertThat(mListener.changedDisplays.size()).isEqualTo(1); + mListener.changedDisplays.clear(); + assertThat(displayDevice.getDisplayDeviceInfoLocked().state).isEqualTo(Display.STATE_OFF); + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState).isNotEqualTo( + Display.STATE_OFF); + verify(mSurfaceControlProxy, never()).setDisplayPowerMode(display.token, Display.STATE_OFF); + + // Execute powerstate change. + changeStateRunnable.run(); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + + // Verify that committed triggered a new change event and is set correctly. + verify(mSurfaceControlProxy, never()).setDisplayPowerMode(display.token, Display.STATE_OFF); + assertThat(mListener.changedDisplays.size()).isEqualTo(1); + assertThat(displayDevice.getDisplayDeviceInfoLocked().state).isEqualTo(Display.STATE_OFF); + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState).isEqualTo( + Display.STATE_OFF); + } + + @Test public void testAfterDisplayChange_GameContentTypeSupportIsUpdated() throws Exception { FakeDisplay display = new FakeDisplay(PORT_A); display.dynamicInfo.gameContentTypeSupported = true; diff --git a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java index b9551999cacb..bc9e9bb9bce8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java @@ -38,7 +38,6 @@ import com.android.internal.R; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -191,7 +190,6 @@ public class DisplayWhiteBalanceTintControllerTest { * Matrix should match the precalculated one for given cct and display primaries. */ @Test - @Ignore public void displayWhiteBalance_validateTransformMatrix() { DisplayPrimaries displayPrimaries = new DisplayPrimaries(); displayPrimaries.red = new CieXyz(); @@ -226,12 +224,12 @@ public class DisplayWhiteBalanceTintControllerTest { float[] matrixDwb = mDisplayWhiteBalanceTintController.getMatrix(); final float[] expectedMatrixDwb = { - 0.962880f, -0.001780f, -0.000158f, 0.0f, - 0.035765f, 0.929988f, 0.000858f, 0.0f, - 0.001354f, -0.000470f, 0.948327f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f + 0.971848f, -0.001421f, 0.000491f, 0.0f, + 0.028193f, 0.945798f, 0.003207f, 0.0f, + -0.000042f, -0.000989f, 0.988659f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; - assertArrayEquals("Unexpected DWB matrix", matrixDwb, expectedMatrixDwb, + assertArrayEquals("Unexpected DWB matrix", expectedMatrixDwb, matrixDwb, 1e-6f /* tolerance */); } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt index cf6c82f23730..0d2e666e431e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt @@ -29,6 +29,7 @@ import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never +import org.mockito.Mockito.times import org.mockito.Mockito.verify @RunWith(JUnit4::class) @@ -192,4 +193,70 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable()) } + + @Test + fun sendDistractingPackagesChanged_withSameVisibilityAllowList() { + mockAllowList(packageSetting1, allowList(10001, 10002, 10003)) + mockAllowList(packageSetting2, allowList(10001, 10002, 10003)) + + distractingPackageHelper.sendDistractingPackagesChanged(pms.snapshotComputer(), + packagesToChange, uidsToChange, TEST_USER_ID, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) + testHandler.flush() + verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), + nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), + nullable(), nullable(), nullable()) + + var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) + var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST) + assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) + assertThat(changedUids).asList().containsExactly( + packageSetting1.appId, packageSetting2.appId) + } + + @Test + fun sendDistractingPackagesChanged_withDifferentVisibilityAllowList() { + mockAllowList(packageSetting1, allowList(10001, 10002, 10003)) + mockAllowList(packageSetting2, allowList(10001, 10002, 10004)) + + distractingPackageHelper.sendDistractingPackagesChanged(pms.snapshotComputer(), + packagesToChange, uidsToChange, TEST_USER_ID, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) + testHandler.flush() + verify(broadcastHelper, times(2)).sendPackageBroadcast( + eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(), + anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable()) + + bundleCaptor.allValues.forEach { + var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) + var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST) + assertThat(changedPackages?.size).isEqualTo(1) + assertThat(changedUids?.size).isEqualTo(1) + assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2) + assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId) + } + } + + @Test + fun sendDistractingPackagesChanged_withNullVisibilityAllowList() { + mockAllowList(packageSetting1, allowList(10001, 10002, 10003)) + mockAllowList(packageSetting2, null /* list */) + + distractingPackageHelper.sendDistractingPackagesChanged(pms.snapshotComputer(), + packagesToChange, uidsToChange, TEST_USER_ID, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) + testHandler.flush() + verify(broadcastHelper, times(2)).sendPackageBroadcast( + eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(), + anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable()) + + bundleCaptor.allValues.forEach { + var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) + var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST) + assertThat(changedPackages?.size).isEqualTo(1) + assertThat(changedUids?.size).isEqualTo(1) + assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2) + assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId) + } + } }
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt index bd012fc7837e..f48d16f91af1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt @@ -20,6 +20,8 @@ import android.os.Build import android.os.Bundle import android.os.UserHandle import android.os.UserManager +import android.util.ArrayMap +import android.util.SparseArray import com.android.server.pm.pkg.PackageStateInternal import com.android.server.testutils.TestHandler import com.android.server.testutils.any @@ -31,6 +33,7 @@ import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.argThat import org.mockito.Mockito.spy import org.mockito.MockitoAnnotations @@ -138,4 +141,15 @@ open class PackageHelperTestBase { rule.system().validateFinalState() return pms } + + protected fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply { + this.put(TEST_USER_ID, uids) + } + + protected fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) { + whenever(rule.mocks().appsFilter.getVisibilityAllowList( + argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java), + any() as ArrayMap<String, out PackageStateInternal> + )).thenReturn(list) + } }
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index 3ba9ca591fb3..121cbe3c7a45 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -20,19 +20,14 @@ import android.content.Intent import android.content.pm.SuspendDialogInfo import android.os.Binder import android.os.PersistableBundle -import android.util.ArrayMap -import android.util.SparseArray -import com.android.server.pm.pkg.PackageStateInternal import com.android.server.testutils.any import com.android.server.testutils.eq import com.android.server.testutils.nullable -import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt -import org.mockito.Mockito.argThat import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -382,16 +377,4 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { assertThat(modifiedUids).asList().containsExactly( packageSetting1.appId, packageSetting2.appId) } - - private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply { - this.put(TEST_USER_ID, uids) - } - - private fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) { - whenever(rule.mocks().appsFilter.getVisibilityAllowList( - argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java), - any() as ArrayMap<String, out PackageStateInternal> - )) - .thenReturn(list) - } } diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index d5c5745d6680..f28ad79c8488 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -37,8 +37,8 @@ import android.accounts.AccountManagerInternal; import android.accounts.CantAddAccountActivity; import android.accounts.IAccountManagerResponse; import android.app.AppOpsManager; -import android.app.PropertyInvalidatedCache; import android.app.INotificationManager; +import android.app.PropertyInvalidatedCache; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.BroadcastReceiver; @@ -253,6 +253,26 @@ public class AccountManagerServiceTest extends AndroidTestCase { } @SmallTest + public void testCheckAddAccountLongName() throws Exception { + unlockSystemUser(); + String longString = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaa"; + Account a11 = new Account(longString, AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1); + + mAms.addAccountExplicitly( + a11, /* password= */ "p11", /* extras= */ null, /* callerPackage= */ null); + + String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE}; + when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list); + Account[] accounts = mAms.getAccountsAsUser(null, + UserHandle.getCallingUserId(), mContext.getOpPackageName()); + assertEquals(0, accounts.length); + } + + + @SmallTest public void testPasswords() throws Exception { unlockSystemUser(); Account a11 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1); diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java new file mode 100644 index 000000000000..049c745fc128 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2022 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 static com.google.common.truth.Truth.assertWithMessage; + +import android.app.backup.BackupAgent; +import android.platform.test.annotations.Presubmit; +import android.system.OsConstants; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.backup.FileMetadata; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class FullRestoreEngineTest { + private static final String DEFAULT_PACKAGE_NAME = "package"; + private static final String DEFAULT_DOMAIN_NAME = "domain"; + private static final String NEW_PACKAGE_NAME = "new_package"; + private static final String NEW_DOMAIN_NAME = "new_domain"; + + private FullRestoreEngine mRestoreEngine; + + @Before + public void setUp() { + mRestoreEngine = new FullRestoreEngine(); + } + + @Test + public void shouldSkipReadOnlyDir_skipsAllReadonlyDirsAndTheirChildren() { + // Create the file tree. + TestFile[] testFiles = new TestFile[] { + TestFile.dir("root"), + TestFile.file("root/auth_token"), + TestFile.dir("root/media"), + TestFile.file("root/media/picture1.png"), + TestFile.file("root/push_token.txt"), + TestFile.dir("root/read-only-dir-1").markReadOnly().expectSkipped(), + TestFile.dir("root/read-only-dir-1/writable-subdir").expectSkipped(), + TestFile.file("root/read-only-dir-1/writable-subdir/writable-file").expectSkipped(), + TestFile.dir("root/read-only-dir-1/writable-subdir/read-only-subdir-2") + .markReadOnly().expectSkipped(), + TestFile.file("root/read-only-dir-1/writable-file").expectSkipped(), + TestFile.file("root/random-stuff.txt"), + TestFile.dir("root/database"), + TestFile.file("root/database/users.db"), + TestFile.dir("root/read-only-dir-2").markReadOnly().expectSkipped(), + TestFile.file("root/read-only-dir-2/writable-file-1").expectSkipped(), + TestFile.file("root/read-only-dir-2/writable-file-2").expectSkipped(), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + @Test + public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSamePackage() { + TestFile[] testFiles = new TestFile[]{ + TestFile.dir("read-only-dir").markReadOnly().expectSkipped(), + TestFile.file("read-only-dir/file").expectSkipped(), + TestFile.file("read-only-dir/file-from-different-package") + .setPackage(NEW_PACKAGE_NAME), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + @Test + public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSameDomain() { + TestFile[] testFiles = new TestFile[]{ + TestFile.dir("read-only-dir").markReadOnly().expectSkipped(), + TestFile.file("read-only-dir/file").expectSkipped(), + TestFile.file("read-only-dir/file-from-different-domain") + .setDomain(NEW_DOMAIN_NAME), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + private void assertCorrectItemsAreSkipped(TestFile[] testFiles) { + // Verify all directories marked with .expectSkipped are skipped. + for (TestFile testFile : testFiles) { + boolean actualExcluded = mRestoreEngine.shouldSkipReadOnlyDir(testFile.mMetadata); + boolean expectedExcluded = testFile.mShouldSkip; + assertWithMessage(testFile.mMetadata.path).that(actualExcluded).isEqualTo( + expectedExcluded); + } + } + + private static class TestFile { + private final FileMetadata mMetadata; + private boolean mShouldSkip; + + static TestFile dir(String path) { + return new TestFile(path, BackupAgent.TYPE_DIRECTORY); + } + + static TestFile file(String path) { + return new TestFile(path, BackupAgent.TYPE_FILE); + } + + TestFile markReadOnly() { + mMetadata.mode = 0; + return this; + } + + TestFile expectSkipped() { + mShouldSkip = true; + return this; + } + + TestFile setPackage(String packageName) { + mMetadata.packageName = packageName; + return this; + } + + TestFile setDomain(String domain) { + mMetadata.domain = domain; + return this; + } + + private TestFile(String path, int type) { + FileMetadata metadata = new FileMetadata(); + metadata.path = path; + metadata.type = type; + metadata.packageName = DEFAULT_PACKAGE_NAME; + metadata.domain = DEFAULT_DOMAIN_NAME; + metadata.mode = OsConstants.S_IWUSR; // Mark as writable. + mMetadata = metadata; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java new file mode 100644 index 000000000000..282c782e752a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.fingerprint.aidl; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.Fingerprint; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.annotation.NonNull; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Presubmit +@SmallTest +public class FingerprintInternalCleanupClientTest { + + private static final int SENSOR_ID = 22; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private AidlSession mAidlSession; + @Mock + private ISession mSession; + @Mock + private BiometricLogger mLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private FingerprintUtils mFingerprintUtils; + @Mock + private ClientMonitorCallback mCallback; + + private FingerprintInternalCleanupClient mClient; + private List<Integer> mAddedIds; + + @Before + public void setup() { + when(mAidlSession.getSession()).thenReturn(mSession); + mAddedIds = new ArrayList<>(); + } + + @Ignore("TODO(b/229015801): verify cleanup behavior") + @Test + public void removesUnknownTemplate() throws Exception { + mClient = createClient(); + + final List<Fingerprint> templates = List.of( + new Fingerprint("one", 1, 1), + new Fingerprint("two", 2, 1) + ); + mClient.start(mCallback); + for (int i = templates.size() - 1; i >= 0; i--) { + mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i); + } + for (int i = templates.size() - 1; i >= 0; i--) { + mClient.getCurrentRemoveClient().onRemoved(templates.get(i), 0); + } + + assertThat(mAddedIds).isEmpty(); + final ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class); + verify(mSession, times(2)).removeEnrollments(captor.capture()); + assertThat(captor.getAllValues().stream() + .flatMap(x -> Arrays.stream(x).boxed()) + .collect(Collectors.toList())) + .containsExactly(1, 2); + verify(mCallback).onClientFinished(eq(mClient), eq(true)); + } + + @Test + public void addsUnknownTemplateWhenVirtualIsEnabled() throws Exception { + mClient = createClient(); + mClient.setFavorHalEnrollments(); + + final List<Fingerprint> templates = List.of( + new Fingerprint("one", 1, 1), + new Fingerprint("two", 2, 1) + ); + mClient.start(mCallback); + for (int i = templates.size() - 1; i >= 0; i--) { + mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i); + } + + assertThat(mAddedIds).containsExactly(1, 2); + verify(mSession, never()).removeEnrollments(any()); + verify(mCallback).onClientFinished(eq(mClient), eq(true)); + } + + protected FingerprintInternalCleanupClient createClient() { + final List<Fingerprint> enrollments = new ArrayList<>(); + final Map<Integer, Long> authenticatorIds = new HashMap<>(); + return new FingerprintInternalCleanupClient(mContext, () -> mAidlSession, 2 /* userId */, + "the.test.owner", SENSOR_ID, mLogger, mBiometricContext, enrollments, + mFingerprintUtils, authenticatorIds) { + @Override + protected void onAddUnknownTemplate(int userId, + @NonNull BiometricAuthenticator.Identifier identifier) { + mAddedIds.add(identifier.getBiometricId()); + } + }; + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index fe3034dc569d..ce7b367ec7ec 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -243,6 +243,7 @@ public final class DeviceStateManagerServiceTest { assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier()); } + @FlakyTest(bugId = 223153452) @Test public void registerCallback() throws RemoteException { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 484b5a8dae1a..0f6addb452a1 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -982,7 +982,11 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - public void handleOnInitializeCecComplete_ByScreenOn() { + public void handleOnInitializeCecComplete_ByScreenOn_PowerControlModeTv() { + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_TV); + mHdmiCecLocalDevicePlayback.onInitializeCecComplete( mHdmiControlService.INITIATED_BY_SCREEN_ON); mTestLooper.dispatchAll(); @@ -999,6 +1003,27 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleOnInitializeCecComplete_ByScreenOn_PowerControlModeNone() { + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.POWER_CONTROL_MODE_NONE); + + mHdmiCecLocalDevicePlayback.onInitializeCecComplete( + mHdmiControlService.INITIATED_BY_SCREEN_ON); + mTestLooper.dispatchAll(); + + HdmiCecMessage activeSource = + HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress); + HdmiCecMessage textViewOn = + HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress, + ADDR_TV); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSource); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn); + } + + @Test public void handleOnInitializeCecComplete_ByWakeUpMessage() { mHdmiCecLocalDevicePlayback.onInitializeCecComplete( mHdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE); diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index c7a903be3bd2..11acfb7af32a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -386,7 +386,8 @@ public class ApexManagerTest { ApexManager.MATCH_ACTIVE_PACKAGE); assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath()); assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2); - assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0); + assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) + .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) .isEqualTo(ApplicationInfo.FLAG_INSTALLED); @@ -396,7 +397,8 @@ public class ApexManagerTest { assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1); assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) .isEqualTo(ApplicationInfo.FLAG_SYSTEM); - assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0); + assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) + .isEqualTo(ApplicationInfo.FLAG_INSTALLED); } @Test @@ -424,7 +426,8 @@ public class ApexManagerTest { ApexManager.MATCH_ACTIVE_PACKAGE); assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath()); assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2); - assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0); + assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) + .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) .isEqualTo(ApplicationInfo.FLAG_INSTALLED); @@ -434,7 +437,8 @@ public class ApexManagerTest { assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1); assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) .isEqualTo(ApplicationInfo.FLAG_SYSTEM); - assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0); + assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) + .isEqualTo(ApplicationInfo.FLAG_INSTALLED); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index e4ee4d064724..fdf9354747a0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -266,6 +266,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { public void sendIntentSender(IntentSender intent) { // Placeholder for spying. } + + @Override + public String getPackageName() { + return SYSTEM_PACKAGE_NAME; + } } /** ShortcutService with injection override methods. */ @@ -704,6 +709,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected UriPermissionOwner mUriPermissionOwner; + protected static final String SYSTEM_PACKAGE_NAME = "android"; protected static final String CALLING_PACKAGE_1 = "com.android.test.1"; protected static final int CALLING_UID_1 = 10001; diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java index 07cca0ca6ba0..4d1c58d2c274 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java @@ -610,7 +610,7 @@ public class PackageParserLegacyCoreTest { assertNotNull(pi.signingInfo); assertTrue(pi.signingInfo.getApkContentsSigners().length > 0); assertTrue(pi.isApex); - assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0); + assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0); } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index fbcad62988be..ae4ff86320e6 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -913,27 +913,6 @@ public class PowerManagerServiceTest { } @Test - public void testInattentiveSleep_dreamEnds_goesToSleepAfterTimeout() { - setMinimumScreenOffTimeoutConfig(5); - setAttentiveTimeout(30000); - createService(); - startSystem(); - - advanceTime(10000); - forceDream(); - advanceTime(10000); - final String pkg = mContextSpy.getOpPackageName(); - mService.getBinderServiceInstance().wakeUp(mClock.now(), - PowerManager.WAKE_REASON_DREAM_FINISHED, "PowerManagerServiceTest:DREAM_FINISHED", - pkg); - advanceTime(10001); - - assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); - assertThat(mService.getBinderServiceInstance().getLastSleepReason()).isEqualTo( - PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE); - } - - @Test public void testInattentiveSleep_goesToSleepWithWakeLock() { final String pkg = mContextSpy.getOpPackageName(); final Binder token = new Binder(); @@ -956,6 +935,27 @@ public class PowerManagerServiceTest { } @Test + public void testInattentiveSleep_dreamEnds_goesToSleepAfterTimeout() { + setMinimumScreenOffTimeoutConfig(5); + setAttentiveTimeout(30000); + createService(); + startSystem(); + + advanceTime(10000); + forceDream(); + advanceTime(10000); + final String pkg = mContextSpy.getOpPackageName(); + mService.getBinderServiceInstance().wakeUp(mClock.now(), + PowerManager.WAKE_REASON_DREAM_FINISHED, "PowerManagerServiceTest:DREAM_FINISHED", + pkg); + advanceTime(10001); + + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + assertThat(mService.getBinderServiceInstance().getLastSleepReason()).isEqualTo( + PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE); + } + + @Test public void testInattentiveSleep_wakeLockOnAfterRelease_inattentiveSleepTimeoutNotAffected() { final DisplayInfo info = new DisplayInfo(); info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java index bd7186e74354..a7d18eeba058 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java @@ -126,7 +126,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { when(file.getAbsolutePath()).thenReturn(String.valueOf(i)); AtomicFile af = new AtomicFile(file); expectedFiles.add(af); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); } cal.add(Calendar.DATE, -1 * retainDays); @@ -136,7 +136,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { when(file.getName()).thenReturn(String.valueOf(cal.getTimeInMillis() - i)); when(file.getAbsolutePath()).thenReturn(String.valueOf(cal.getTimeInMillis() - i)); AtomicFile af = new AtomicFile(file); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); } // back to today; trim everything a day + old @@ -162,7 +162,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { when(file.getName()).thenReturn(i + ".bak"); when(file.getAbsolutePath()).thenReturn(i + ".bak"); AtomicFile af = new AtomicFile(file); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); } // trim everything a day+ old @@ -224,7 +224,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { public void testReadNotificationHistory_readsAllFiles() throws Exception { for (long i = 10; i >= 5; i--) { AtomicFile af = mock(AtomicFile.class); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); } mDataBase.readNotificationHistory(); @@ -248,11 +248,11 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { public void testReadNotificationHistory_withNumFilterDoesNotReadExtraFiles() throws Exception { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); AtomicFile af2 = mock(AtomicFile.class); when(af2.getBaseFile()).thenReturn(new File(mRootDir, "af2")); - mDataBase.mHistoryFiles.addLast(af2); + mDataBase.mHistoryFiles.add(af2); mDataBase.readNotificationHistory(null, null, 0); @@ -269,7 +269,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(true); @@ -292,7 +292,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(false); @@ -315,7 +315,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); when(nh.removeConversationsFromWrite("pkg", Set.of("convo", "another"))).thenReturn(true); @@ -338,7 +338,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); when(nh.removeConversationsFromWrite("pkg", Set.of("convo"))).thenReturn(false); @@ -361,7 +361,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); when(nh.removeChannelFromWrite("pkg", "channel")).thenReturn(true); @@ -384,7 +384,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af")); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); when(nh.removeChannelFromWrite("pkg", "channel")).thenReturn(false); @@ -424,7 +424,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { for (int i = 0; i < 5; i++) { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i)); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); } // Baseline size of history files assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(5); @@ -440,7 +440,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { for (int i = 0; i < 5; i++) { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i)); - mDataBase.mHistoryFiles.addLast(af); + mDataBase.mHistoryFiles.add(af); } // Baseline size of history files assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(5); diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java index dd0c162d8467..a917c57446a9 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java @@ -122,8 +122,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase { when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid())) .thenReturn(PERMISSION_GRANTED); mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), - mContext.getPackageName(), Process.myPid(), - Process.myUid(), testPerms); + Process.myPid(), Process.myUid(), testPerms); verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid())); verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid())); @@ -148,7 +147,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase { private void grantSlicePermission() { doReturn(PERMISSION_GRANTED).when(mService).checkSlicePermission( - eq(TEST_URI), anyString(), anyString(), anyInt(), anyInt(), any()); + eq(TEST_URI), anyString(), anyInt(), anyInt(), any()); doReturn(PERMISSION_GRANTED).when(mService).checkAccess( anyString(), eq(TEST_URI), anyInt(), anyInt()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index 3beb7f2049df..88c7017e9942 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -283,6 +283,21 @@ public class DimmerTests extends WindowTestsBase { verify(mTransaction).remove(dimLayer); } + @Test + public void testDimmerWithBlurUpdatesTransaction() { + TestWindowContainer child = new TestWindowContainer(mWm); + mHost.addChild(child, 0); + + final int blurRadius = 50; + mDimmer.dimBelow(mTransaction, child, 0, blurRadius); + SurfaceControl dimLayer = getDimLayer(); + + assertNotNull("Dimmer should have created a surface", dimLayer); + + verify(mTransaction).setBackgroundBlurRadius(dimLayer, blurRadius); + verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, -1); + } + private SurfaceControl getDimLayer() { return mDimmer.mDimState.mDimLayer; } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 5b6e6863c0df..df82f244a682 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -516,6 +516,7 @@ final class HotwordDetectionConnection { if (mValidatingDspTrigger) { mValidatingDspTrigger = false; enforcePermissionsForDataDelivery(); + enforceExtraKeyphraseIdNotLeaked(result, recognitionEvent); externalCallback.onKeyphraseDetected(recognitionEvent, result); if (result != null) { Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result) @@ -591,6 +592,7 @@ final class HotwordDetectionConnection { mValidatingDspTrigger = false; try { enforcePermissionsForDataDelivery(); + enforceExtraKeyphraseIdNotLeaked(result, recognitionEvent); } catch (SecurityException e) { HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, @@ -1143,6 +1145,19 @@ final class HotwordDetectionConnection { } } + private static void enforceExtraKeyphraseIdNotLeaked(HotwordDetectedResult result, + SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) { + // verify the phrase ID in HotwordDetectedResult is not exposing extra phrases + // the DSP did not detect + for (SoundTrigger.KeyphraseRecognitionExtra keyphrase : recognitionEvent.keyphraseExtras) { + if (keyphrase.getKeyphraseId() == result.getHotwordPhraseId()) { + return; + } + } + throw new SecurityException("Ignoring #onDetected due to trusted service " + + "sharing a keyphrase ID which the DSP did not detect"); + } + private static final String OP_MESSAGE = "Providing hotword detection result to VoiceInteractionService"; }; diff --git a/telecomm/java/android/telecom/Logging/EventManager.java b/telecomm/java/android/telecom/Logging/EventManager.java index 1342038c6477..a74c0bb99549 100644 --- a/telecomm/java/android/telecom/Logging/EventManager.java +++ b/telecomm/java/android/telecom/Logging/EventManager.java @@ -180,7 +180,7 @@ public class EventManager { } } - private final List<Event> mEvents = Collections.synchronizedList(new LinkedList<>()); + private final List<Event> mEvents = Collections.synchronizedList(new ArrayList<>()); private final Loggable mRecordEntry; public EventRecord(Loggable recordEntry) { @@ -197,7 +197,7 @@ public class EventManager { } public List<Event> getEvents() { - return new LinkedList<>(mEvents); + return new ArrayList<>(mEvents); } public List<EventTiming> extractEventTimings() { @@ -205,7 +205,7 @@ public class EventManager { return Collections.emptyList(); } - LinkedList<EventTiming> result = new LinkedList<>(); + ArrayList<EventTiming> result = new ArrayList<>(); Map<String, PendingResponse> pendingResponses = new HashMap<>(); synchronized (mEvents) { for (Event event : mEvents) { diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java index b8ad9e2fbe6c..ff87ab00ae8b 100644 --- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java +++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java @@ -359,7 +359,7 @@ public class ParcelableCallAnalytics implements Parcelable { eventTimings = new ArrayList<>(); in.readTypedList(eventTimings, EventTiming.CREATOR); isVideoCall = readByteAsBoolean(in); - videoEvents = new LinkedList<>(); + videoEvents = new ArrayList<>(); in.readTypedList(videoEvents, VideoEvent.CREATOR); callSource = in.readInt(); } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index e0f5b2095190..0f7dde5a18d9 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1179,7 +1179,7 @@ public class TelecomManager { if (service != null) { try { return service.getSimCallManager( - SubscriptionManager.getDefaultSubscriptionId()); + SubscriptionManager.getDefaultSubscriptionId(), mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSimCallManager"); } @@ -1201,7 +1201,7 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - return service.getSimCallManager(subscriptionId); + return service.getSimCallManager(subscriptionId, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSimCallManager"); } @@ -1225,7 +1225,7 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - return service.getSimCallManagerForUser(userId); + return service.getSimCallManagerForUser(userId, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSimCallManagerForUser"); } @@ -1500,7 +1500,7 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - service.registerPhoneAccount(account); + service.registerPhoneAccount(account, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#registerPhoneAccount", e); } @@ -1516,7 +1516,7 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - service.unregisterPhoneAccount(accountHandle); + service.unregisterPhoneAccount(accountHandle, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#unregisterPhoneAccount", e); } @@ -1597,7 +1597,7 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - return service.getDefaultDialerPackage(); + return service.getDefaultDialerPackage(mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e); } @@ -1671,7 +1671,7 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - return service.getSystemDialerPackage(); + return service.getSystemDialerPackage(mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e); } @@ -2076,7 +2076,8 @@ public class TelecomManager { "acceptHandover for API > O-MR1"); return; } - service.addNewIncomingCall(phoneAccount, extras == null ? new Bundle() : extras); + service.addNewIncomingCall(phoneAccount, extras == null ? new Bundle() : extras, + mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e); } @@ -2118,7 +2119,8 @@ public class TelecomManager { if (service != null) { try { service.addNewIncomingConference( - phoneAccount, extras == null ? new Bundle() : extras); + phoneAccount, extras == null ? new Bundle() : extras, + mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e); } @@ -2414,7 +2416,7 @@ public class TelecomManager { Intent result = null; if (service != null) { try { - result = service.createManageBlockedNumbersIntent(); + result = service.createManageBlockedNumbersIntent(mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#createManageBlockedNumbersIntent", e); } @@ -2571,7 +2573,7 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - service.acceptHandover(srcAddr, videoState, destAcct); + service.acceptHandover(srcAddr, videoState, destAcct, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "RemoteException acceptHandover: " + e); } diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 37403a806daf..101280abce85 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -106,22 +106,22 @@ interface ITelecomService { /** * @see TelecomServiceImpl#getSimCallManager */ - PhoneAccountHandle getSimCallManager(int subId); + PhoneAccountHandle getSimCallManager(int subId, String callingPackage); /** * @see TelecomServiceImpl#getSimCallManagerForUser */ - PhoneAccountHandle getSimCallManagerForUser(int userId); + PhoneAccountHandle getSimCallManagerForUser(int userId, String callingPackage); /** * @see TelecomServiceImpl#registerPhoneAccount */ - void registerPhoneAccount(in PhoneAccount metadata); + void registerPhoneAccount(in PhoneAccount metadata, String callingPackage); /** * @see TelecomServiceImpl#unregisterPhoneAccount */ - void unregisterPhoneAccount(in PhoneAccountHandle account); + void unregisterPhoneAccount(in PhoneAccountHandle account, String callingPackage); /** * @see TelecomServiceImpl#clearAccounts @@ -154,7 +154,7 @@ interface ITelecomService { /** * @see TelecomServiceImpl#getDefaultDialerPackage */ - String getDefaultDialerPackage(); + String getDefaultDialerPackage(String callingPackage); /** * @see TelecomServiceImpl#getDefaultDialerPackage @@ -164,7 +164,7 @@ interface ITelecomService { /** * @see TelecomServiceImpl#getSystemDialerPackage */ - String getSystemDialerPackage(); + String getSystemDialerPackage(String callingPackage); /** * @see TelecomServiceImpl#dumpCallAnalytics @@ -262,12 +262,15 @@ interface ITelecomService { /** * @see TelecomServiceImpl#addNewIncomingCall */ - void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras); + void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras, + String callingPackage); /** * @see TelecomServiceImpl#addNewIncomingConference */ - void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras); + void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras, + String callingPackage); + /** * @see TelecomServiceImpl#addNewUnknownCall @@ -303,7 +306,7 @@ interface ITelecomService { /** * @see TelecomServiceImpl#createManageBlockedNumbersIntent **/ - Intent createManageBlockedNumbersIntent(); + Intent createManageBlockedNumbersIntent(String callingPackage); /** * @see TelecomServiceImpl#createLaunchEmergencyDialerIntent @@ -330,7 +333,8 @@ interface ITelecomService { /** * @see TelecomServiceImpl#acceptHandover */ - void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct); + void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct, + String callingPackage); /** * @see TelecomServiceImpl#setTestEmergencyPhoneAccountPackageNameFilter diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 9c886aada728..f4d11a89f92a 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -176,7 +176,7 @@ public class CarrierConfigManager { * This flag specifies whether VoLTE availability is based on provisioning. By default this is * false. * Used for UCE to determine if EAB provisioning checks should be based on provisioning. - * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead. + * @deprecated Use {@link Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL} instead. */ @Deprecated public static final String diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 71ffd6e2ec9f..999b55a40ca6 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -7744,7 +7744,7 @@ public class TelephonyManager { * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * TODO: remove this one. use {@link #rebootModem()} for reset type 1 and - * {@link #resetRadioConfig()} for reset type 3 + * {@link #resetRadioConfig()} for reset type 3 (b/116476729) * * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset * @return true on success; false on any failure. diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 5bae1ad72d93..f8048aa3731c 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -539,7 +539,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. @@ -601,7 +600,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. @@ -649,7 +647,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. @@ -862,7 +859,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. @@ -937,7 +933,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. @@ -1111,7 +1106,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. @@ -1226,7 +1220,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. @@ -1415,7 +1408,6 @@ public class ImsMmTelManager implements RegistrationManager { * <li>The caller has carrier privileges (see * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any * active subscription.</li> - * <li>The caller is the default SMS app for the device.</li> * </ul> * <p>The profile owner is an app that owns a managed profile on the device; for more details * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 34158685b6d1..4439e5c35d83 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -18,6 +18,7 @@ package android.telephony.ims; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; @@ -43,6 +44,8 @@ import android.util.Log; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.ITelephony; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -87,6 +90,46 @@ public class ImsRcsManager { "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; /** + * This carrier supports User Capability Exchange as, defined by the framework using a + * presence server. If set, the RcsFeature should support capability exchange. If not set, this + * RcsFeature should not publish capabilities or service capability requests. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = { + CAPABILITY_TYPE_NONE, + CAPABILITY_TYPE_OPTIONS_UCE, + CAPABILITY_TYPE_PRESENCE_UCE + }) + public @interface RcsImsCapabilityFlag {} + + /** + * Undefined capability type for initialization + */ + public static final int CAPABILITY_TYPE_NONE = 0; + + /** + * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the + * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS. + * If not set, this RcsFeature should not service capability requests. + */ + public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0; + + /** + * This carrier supports User Capability Exchange using a presence server as defined by the + * framework. If set, the RcsFeature should support capability exchange using a presence + * server. If not set, this RcsFeature should not publish capabilities or service capability + * requests using presence. + */ + public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; + + /** + * This is used to check the upper range of RCS capability + * @hide + */ + public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1; + + /** * An application can use {@link #addOnAvailabilityChangedListener} to register a * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature * availability status updates from the ImsService. @@ -104,7 +147,7 @@ public class ImsRcsManager { * * @param capabilities The new availability of the capabilities. */ - void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities); + void onAvailabilityChanged(@RcsImsCapabilityFlag int capabilities); } /** @@ -486,7 +529,7 @@ public class ImsRcsManager { */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, + public boolean isCapable(@RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { @@ -497,6 +540,8 @@ public class ImsRcsManager { try { return imsRcsController.isCapable(mSubId, capability, radioTech); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { Log.w(TAG, "Error calling IImsRcsController#isCapable", e); throw new ImsException("Remote IMS Service is not available", @@ -522,7 +567,7 @@ public class ImsRcsManager { */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, + public boolean isAvailable(@RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); @@ -534,6 +579,8 @@ public class ImsRcsManager { try { return imsRcsController.isAvailable(mSubId, capability, radioTech); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { Log.w(TAG, "Error calling IImsRcsController#isAvailable", e); throw new ImsException("Remote IMS Service is not available", diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index be233b82c426..50fcdf86be79 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -52,6 +52,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -188,6 +189,7 @@ public class ImsService extends Service { new SparseArray<>(); private IImsServiceControllerListener mListener; + private final Object mListenerLock = new Object(); private Executor mExecutor; /** @@ -225,7 +227,30 @@ public class ImsService extends Service { protected final IBinder mImsServiceController = new IImsServiceController.Stub() { @Override public void setListener(IImsServiceControllerListener l) { - mListener = l; + synchronized (mListenerLock) { + if (mListener != null && mListener.asBinder().isBinderAlive()) { + try { + mListener.asBinder().unlinkToDeath(mDeathRecipient, 0); + } catch (NoSuchElementException e) { + Log.w(LOG_TAG, "IImsServiceControllerListener does not exist"); + } + } + + mListener = l; + if (mListener == null) { + executeMethodAsync(() -> releaseResource(), "releaseResource"); + return; + } + + try { + mListener.asBinder().linkToDeath(mDeathRecipient, 0); + Log.i(LOG_TAG, "setListener: register linkToDeath"); + } catch (RemoteException e) { + // RemoteException means target binder process was crashed + // release resource + executeMethodAsync(() -> releaseResource(), "releaseResource"); + } + } } @Override @@ -364,28 +389,15 @@ public class ImsService extends Service { ImsService.this.disableImsForSubscription(slotId, subId), "disableIms"); } - // Call the methods with a clean calling identity on the executor and wait indefinitely for - // the future to return. - private void executeMethodAsync(Runnable r, String errorLogName) { - try { - CompletableFuture.runAsync( - () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); - } catch (CancellationException | CompletionException e) { - Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " - + e.getMessage()); - } - } - private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) { - CompletableFuture<T> future = CompletableFuture.supplyAsync( - () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); - try { - return future.get(); - } catch (ExecutionException | InterruptedException e) { - Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " - + e.getMessage()); - return null; - } + }; + + private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Log.w(LOG_TAG, + "IImsServiceControllerListener binder to framework has died. Cleaning up"); + executeMethodAsync(() -> releaseResource(), "releaseResource"); } }; @@ -490,6 +502,9 @@ public class ImsService extends Service { } private void removeImsFeature(int slotId, int featureType) { + // clear cached data + notifySubscriptionRemoved(slotId); + synchronized (mFeaturesBySlot) { // get ImsFeature associated with the slot/feature SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); @@ -507,7 +522,6 @@ public class ImsService extends Service { f.onFeatureRemoved(); features.remove(featureType); } - } /** @@ -552,6 +566,54 @@ public class ImsService extends Service { return createFlag; } + private void releaseResource() { + Log.w(LOG_TAG, "cleaning up features"); + synchronized (mFeaturesBySlot) { + SparseArray<ImsFeature> features; + ImsFeature imsFeature; + + for (int i = 0; i < mFeaturesBySlot.size(); i++) { + features = mFeaturesBySlot.valueAt(i); + if (features == null) { + continue; + } + + for (int index = 0; index < features.size(); index++) { + imsFeature = features.valueAt(index); + if (imsFeature != null) { + imsFeature.onFeatureRemoved(); + } + } + features.clear(); + } + mFeaturesBySlot.clear(); + } + } + + // Call the methods with a clean calling identity on the executor and wait indefinitely for + // the future to return. + private void executeMethodAsync(Runnable r, String errorLogName) { + try { + CompletableFuture.runAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); + } catch (CancellationException | CompletionException e) { + Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " + + e.getMessage()); + } + } + + private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) { + CompletableFuture<T> future = CompletableFuture.supplyAsync( + () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); + try { + return future.get(); + } catch (ExecutionException | InterruptedException e) { + Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " + + e.getMessage()); + return null; + } + } + /** * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService} * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that @@ -574,10 +636,14 @@ public class ImsService extends Service { */ public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) throws RemoteException { - if (mListener == null) { - throw new IllegalStateException("Framework is not ready"); + IImsServiceControllerListener l; + synchronized (mListenerLock) { + if (mListener == null) { + throw new IllegalStateException("Framework is not ready"); + } + l = mListener; } - mListener.onUpdateSupportedImsFeatures(c); + l.onUpdateSupportedImsFeatures(c); } /** @@ -629,6 +695,24 @@ public class ImsService extends Service { } /** + * The subscription has removed. The ImsService should notify ImsRegistrationImplBase and + * ImsConfigImplBase the SIM state was changed. + * @param slotId The slot ID which has removed. + */ + private void notifySubscriptionRemoved(int slotId) { + ImsRegistrationImplBase registrationImplBase = + getRegistration(slotId); + if (registrationImplBase != null) { + registrationImplBase.clearRegistrationCache(); + } + + ImsConfigImplBase imsConfigImplBase = getConfig(slotId); + if (imsConfigImplBase != null) { + imsConfigImplBase.clearConfigurationCache(); + } + } + + /** * The framework has enabled IMS for the slot specified, the ImsService should register for IMS * and perform all appropriate initialization to bring up all ImsFeatures. * @deprecated Use {@link #enableImsForSubscription} instead. diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 677c1a9a7d76..f2976f14d7a6 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -38,7 +38,6 @@ import android.telephony.ims.aidl.IFeatureProvisioningCallback; import android.telephony.ims.aidl.IImsConfigCallback; import android.telephony.ims.aidl.IRcsConfigCallback; import android.telephony.ims.feature.MmTelFeature; -import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsConfigImplBase; import android.telephony.ims.stub.ImsRegistrationImplBase; @@ -55,6 +54,9 @@ import java.util.concurrent.Executor; * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning * applications and may vary. It is up to the carrier and OEM applications to ensure that the * correct provisioning keys are being used when integrating with a vendor's ImsService. + * + * Use {@link android.telephony.ims.ImsManager#getProvisioningManager(int)} to get an instance of + * this manager. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) public class ProvisioningManager { @@ -970,7 +972,7 @@ public class ProvisioningManager { /** * Callback for IMS provisioning feature changes. */ - public static class FeatureProvisioningCallback { + public abstract static class FeatureProvisioningCallback { private static class CallbackBinder extends IFeatureProvisioningCallback.Stub { @@ -1024,12 +1026,10 @@ public class ProvisioningManager { * specified, or {@code false} if the capability is not provisioned for the technology * specified. */ - public void onFeatureProvisioningChanged( + public abstract void onFeatureProvisioningChanged( @MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech, - boolean isProvisioned) { - // Base Implementation - } + boolean isProvisioned); /** * The IMS RCS provisioning has changed for a specific capability and IMS @@ -1041,12 +1041,10 @@ public class ProvisioningManager { * specified, or {@code false} if the capability is not provisioned for the technology * specified. */ - public void onRcsFeatureProvisioningChanged( - @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, + public abstract void onRcsFeatureProvisioningChanged( + @ImsRcsManager.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech, - boolean isProvisioned) { - // Base Implementation - } + boolean isProvisioned); /**@hide*/ public final IFeatureProvisioningCallback getBinder() { @@ -1505,7 +1503,7 @@ public class ProvisioningManager { * Get the provisioning status for the IMS RCS capability specified. * * If provisioning is not required for the queried - * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return + * {@link ImsRcsManager.RcsImsCapabilityFlag} this method will always return * {@code true}. * * @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL @@ -1522,7 +1520,7 @@ public class ProvisioningManager { @WorkerThread @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getRcsProvisioningStatusForCapability( - @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + @ImsRcsManager.RcsImsCapabilityFlag int capability) { try { return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, ImsRegistrationImplBase.REGISTRATION_TECH_LTE); @@ -1535,7 +1533,7 @@ public class ProvisioningManager { * Get the provisioning status for the IMS RCS capability specified. * * If provisioning is not required for the queried - * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method + * {@link ImsRcsManager.RcsImsCapabilityFlag} this method * will always return {@code true}. * * <p> Requires Permission: @@ -1553,7 +1551,7 @@ public class ProvisioningManager { @WorkerThread @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean getRcsProvisioningStatusForCapability( - @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, + @ImsRcsManager.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech) { try { return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, tech); @@ -1590,7 +1588,7 @@ public class ProvisioningManager { @WorkerThread @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRcsProvisioningStatusForCapability( - @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, + @ImsRcsManager.RcsImsCapabilityFlag int capability, boolean isProvisioned) { try { getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability, @@ -1622,7 +1620,7 @@ public class ProvisioningManager { @WorkerThread @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRcsProvisioningStatusForCapability( - @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, + @ImsRcsManager.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) { try { getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability, @@ -1676,7 +1674,7 @@ public class ProvisioningManager { */ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean isRcsProvisioningRequiredForCapability( - @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, + @ImsRcsManager.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int tech) { try { return getITelephony().isRcsProvisioningRequiredForCapability(mSubId, capability, tech); diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 154bb11db5e3..91dc38ff9ddb 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -55,20 +55,28 @@ public class RcsUceAdapter { * This carrier supports User Capability Exchange as, defined by the framework using * SIP OPTIONS. If set, the RcsFeature should support capability exchange. If not set, this * RcsFeature should not publish capabilities or service capability requests. + * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_OPTIONS_UCE} instead. * @hide */ + @Deprecated public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0; /** * This carrier supports User Capability Exchange as, defined by the framework using a * presence server. If set, the RcsFeature should support capability exchange. If not set, this * RcsFeature should not publish capabilities or service capability requests. + * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_PRESENCE_UCE} instead. * @hide */ + @Deprecated @SystemApi public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; - /**@hide*/ + /** + * @deprecated Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead. + * @hide + */ + @Deprecated @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "CAPABILITY_TYPE_", value = { CAPABILITY_TYPE_OPTIONS_UCE, diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index ad2e9e133a11..fb0e659ec77b 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -396,7 +396,7 @@ public class MmTelFeature extends ImsFeature { /** * Undefined capability type for initialization * This is used to check the upper range of MmTel capability - * {@hide} + * @hide */ public static final int CAPABILITY_TYPE_NONE = 0; @@ -427,7 +427,7 @@ public class MmTelFeature extends ImsFeature { /** * This is used to check the upper range of MmTel capability - * {@hide} + * @hide */ public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1; @@ -738,7 +738,7 @@ public class MmTelFeature extends ImsFeature { * Enabling/Disabling a capability here indicates that the capability should be registered or * deregistered (depending on the capability change) and become available or unavailable to * the framework. - * * @hide + * @hide */ @Override @SystemApi diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 70e4ef1f1a3a..843827befb65 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -24,7 +24,7 @@ import android.annotation.SystemApi; import android.content.Context; import android.net.Uri; import android.os.RemoteException; -import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.ImsRcsManager; import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper; import android.telephony.ims.aidl.ICapabilityExchangeEventListener; import android.telephony.ims.aidl.IImsCapabilityCallback; @@ -59,7 +59,9 @@ import java.util.function.Supplier; /** * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend * this class and provide implementations of the RcsFeature methods that they support. + * @hide */ +@SystemApi public class RcsFeature extends ImsFeature { private static final String LOG_TAG = "RcsFeature"; @@ -184,18 +186,22 @@ public class RcsFeature extends ImsFeature { * Contains the capabilities defined and supported by a {@link RcsFeature} in the * form of a bitmask. The capabilities that are used in the RcsFeature are * defined as: - * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE} - * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE} + * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE} + * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE} * * The enabled capabilities of this RcsFeature will be set by the framework - * using {#changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}. + * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}. * After the capabilities have been set, the RcsFeature may then perform the necessary bring up * of the capability and notify the capability status as true using - * {#notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the * framework that the capability is available for usage. */ public static class RcsImsCapabilities extends Capabilities { - /** @hide*/ + + /** + * Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead in case used for public API + * @hide + */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = { CAPABILITY_TYPE_NONE, @@ -226,7 +232,7 @@ public class RcsFeature extends ImsFeature { /** * This is used to check the upper range of RCS capability - * {@hide} + * @hide */ public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1; @@ -234,10 +240,8 @@ public class RcsFeature extends ImsFeature { * Create a new {@link RcsImsCapabilities} instance with the provided capabilities. * @param capabilities The capabilities that are supported for RCS in the form of a * bitfield. - * @hide */ - @SystemApi - public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { + public RcsImsCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) { super(capabilities); } @@ -249,30 +253,18 @@ public class RcsFeature extends ImsFeature { super(capabilities.getMask()); } - /** - * @hide - */ @Override - @SystemApi - public void addCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { + public void addCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) { super.addCapabilities(capabilities); } - /** - * @hide - */ @Override - @SystemApi - public void removeCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { + public void removeCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) { super.removeCapabilities(capabilities); } - /** - * @hide - */ @Override - @SystemApi - public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { + public boolean isCapable(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) { return super.isCapable(capabilities); } } @@ -288,9 +280,7 @@ public class RcsFeature extends ImsFeature { * Method stubs called from the framework will be called asynchronously. To specify the * {@link Executor} that the methods stubs will be called, use * {@link RcsFeature#RcsFeature(Executor)} instead. - * @hide */ - @SystemApi public RcsFeature() { super(); // Run on the Binder threads that call them. @@ -302,9 +292,7 @@ public class RcsFeature extends ImsFeature { * framework. * @param executor The executor for the framework to use when executing the methods overridden * by the implementation of RcsFeature. - * @hide */ - @SystemApi public RcsFeature(@NonNull Executor executor) { super(); if (executor == null) { @@ -335,10 +323,8 @@ public class RcsFeature extends ImsFeature { * requests. To change the status of the capabilities * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. * @return A copy of the current RcsFeature capability status. - * @hide */ @Override - @SystemApi public @NonNull final RcsImsCapabilities queryCapabilityStatus() { return new RcsImsCapabilities(super.queryCapabilityStatus()); } @@ -348,9 +334,7 @@ public class RcsFeature extends ImsFeature { * this signals to the framework that the capability has been initialized and is ready. * Call {@link #queryCapabilityStatus()} to return the current capability status. * @param capabilities The current capability status of the RcsFeature. - * @hide */ - @SystemApi public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) { if (capabilities == null) { throw new IllegalArgumentException("RcsImsCapabilities must be non-null!"); @@ -367,11 +351,9 @@ public class RcsFeature extends ImsFeature { * @param capability The capability that we are querying the configuration for. * @param radioTech The radio technology type that we are querying. * @return true if the capability is enabled, false otherwise. - * @hide */ - @SystemApi public boolean queryCapabilityConfiguration( - @RcsUceAdapter.RcsImsCapabilityFlag int capability, + @ImsRcsManager.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { // Base Implementation - Override to provide functionality return false; @@ -392,10 +374,8 @@ public class RcsFeature extends ImsFeature { * be called for each capability change that resulted in an error. * @param request The request to change the capability. * @param callback To notify the framework that the result of the capability changes. - * @hide */ @Override - @SystemApi public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy callback) { // Base Implementation - Override to provide functionality @@ -415,9 +395,7 @@ public class RcsFeature extends ImsFeature { * event to the framework. * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability * exchange if it is supported by the device. - * @hide */ - @SystemApi public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl( @NonNull CapabilityExchangeEventListener listener) { // Base Implementation, override to implement functionality @@ -427,28 +405,20 @@ public class RcsFeature extends ImsFeature { /** * Remove the given CapabilityExchangeImplBase instance. * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed. - * @hide */ - @SystemApi public void destroyCapabilityExchangeImpl( @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) { // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance. } - /**{@inheritDoc} - * @hide - */ + /**{@inheritDoc}*/ @Override - @SystemApi public void onFeatureRemoved() { } - /**{@inheritDoc} - * @hide - */ + /**{@inheritDoc}*/ @Override - @SystemApi public void onFeatureReady() { } diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index f371ec3a28a7..897b57f48dad 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -481,6 +481,17 @@ public class ImsConfigImplBase { } } + /** + * Clear cached configuration value. + */ + public void clearCachedValue() { + Log.i(TAG, "clearCachedValue"); + synchronized (mLock) { + mProvisionedIntValue.clear(); + mProvisionedStringValue.clear(); + } + } + // Call the methods with a clean calling identity on the executor and wait indefinitely for // the future to return. private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { @@ -538,6 +549,7 @@ public class ImsConfigImplBase { private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks = new RemoteCallbackListExt<>(); private byte[] mRcsConfigData; + private final Object mRcsConfigDataLock = new Object(); ImsConfigStub mImsConfigStub; /** @@ -616,12 +628,20 @@ public class ImsConfigImplBase { private void addRcsConfigCallback(IRcsConfigCallback c) { mRcsCallbacks.register(c); - if (mRcsConfigData != null) { - try { - c.onConfigurationChanged(mRcsConfigData); - } catch (RemoteException e) { - Log.w(TAG, "dead binder to call onConfigurationChanged, skipping."); + + // This is used to avoid calling the binder out of the synchronized scope. + byte[] cloneRcsConfigData; + synchronized (mRcsConfigDataLock) { + if (mRcsConfigData == null) { + return; } + cloneRcsConfigData = mRcsConfigData.clone(); + } + + try { + c.onConfigurationChanged(cloneRcsConfigData); + } catch (RemoteException e) { + Log.w(TAG, "dead binder to call onConfigurationChanged, skipping."); } } @@ -631,18 +651,23 @@ public class ImsConfigImplBase { private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { // cache uncompressed config - config = isCompressed ? RcsConfig.decompressGzip(config) : config; - if (Arrays.equals(mRcsConfigData, config)) { - return; + final byte[] rcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config; + + synchronized (mRcsConfigDataLock) { + if (Arrays.equals(mRcsConfigData, config)) { + return; + } + mRcsConfigData = rcsConfigData; } - mRcsConfigData = config; // can be null in testing if (mRcsCallbacks != null) { synchronized (mRcsCallbacks) { mRcsCallbacks.broadcastAction(c -> { try { - c.onConfigurationChanged(mRcsConfigData); + // config is cloned here so modifications to the config passed to the + // vendor do not accidentally modify the cache. + c.onConfigurationChanged(rcsConfigData.clone()); } catch (RemoteException e) { Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping."); } @@ -653,7 +678,9 @@ public class ImsConfigImplBase { } private void onNotifyRcsAutoConfigurationRemoved() { - mRcsConfigData = null; + synchronized (mRcsConfigDataLock) { + mRcsConfigData = null; + } if (mRcsCallbacks != null) { synchronized (mRcsCallbacks) { mRcsCallbacks.broadcastAction(c -> { @@ -857,4 +884,17 @@ public class ImsConfigImplBase { mImsConfigStub.mExecutor = executor; } } + + /** + * Clear all cached config data. This will be called when the config data is no longer valid + * such as when the SIM was removed. + * @hide + */ + public final void clearConfigurationCache() { + mImsConfigStub.clearCachedValue(); + + synchronized (mRcsConfigDataLock) { + mRcsConfigData = null; + } + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index ac5565bea810..6fc1cc828a2c 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -93,7 +93,7 @@ public class ImsRegistrationImplBase { /** * This is used to check the upper range of registration tech - * {@hide} + * @hide */ public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_NR + 1; @@ -517,4 +517,16 @@ public class ImsRegistrationImplBase { mExecutor = executor; } } + + /** + * Clear the cached data when the subscription is no longer valid + * such as when a sim is removed. + * @hide + */ + public final void clearRegistrationCache() { + synchronized (mLock) { + mUris = null; + mUrisSet = false; + } + } } diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java index 7490d3f2b50c..ab83997c67fc 100644 --- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java @@ -114,6 +114,63 @@ public class StagedInstallInternalTest { Uninstall.packages(TestApp.A, TestApp.B); } + private boolean isSystem(PackageInfo info) { + return (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + private boolean isUpdatedSystem(PackageInfo info) { + return (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; + } + + @Test + public void testUpdateSystemApp_InstallV2() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + + PackageManager pm = + InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); + PackageInfo info; + // Check factory version + info = pm.getPackageInfo(TestApp.A, + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY)); + assertThat(isSystem(info)).isTrue(); + assertThat(isUpdatedSystem(info)).isFalse(); + // Check active version + info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0)); + assertThat(isSystem(info)).isTrue(); + assertThat(isUpdatedSystem(info)).isFalse(); + + Install.single(TestApp.A2).commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + + // Check factory version + info = pm.getPackageInfo(TestApp.A, + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY)); + assertThat(isSystem(info)).isTrue(); + assertThat(isUpdatedSystem(info)).isFalse(); + // Check active version + info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0)); + assertThat(isSystem(info)).isTrue(); + assertThat(isUpdatedSystem(info)).isTrue(); + } + + @Test + public void testUpdateSystemApp_PostInstallV2() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + + PackageManager pm = + InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); + PackageInfo info; + // Check factory version + info = pm.getPackageInfo(TestApp.A, + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY)); + assertThat(isSystem(info)).isTrue(); + assertThat(isUpdatedSystem(info)).isFalse(); + // Check active version + info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0)); + assertThat(isSystem(info)).isTrue(); + assertThat(isUpdatedSystem(info)).isTrue(); + } + @Test public void testDuplicateApkInApexShouldFail_Commit() throws Exception { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -403,7 +460,8 @@ public class StagedInstallInternalTest { { PackageInfo apex = pm.getPackageInfo("test.apex.rebootless", PackageManager.MATCH_APEX); assertThat(apex.getLongVersionCode()).isEqualTo(1); - assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0); + assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) + .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) .isEqualTo(ApplicationInfo.FLAG_INSTALLED); assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active"); @@ -414,7 +472,8 @@ public class StagedInstallInternalTest { assertThat(apex.getLongVersionCode()).isEqualTo(1); assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) .isEqualTo(ApplicationInfo.FLAG_SYSTEM); - assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0); + assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) + .isEqualTo(ApplicationInfo.FLAG_INSTALLED); assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex"); } @@ -425,7 +484,8 @@ public class StagedInstallInternalTest { { PackageInfo apex = pm.getPackageInfo("test.apex.rebootless", PackageManager.MATCH_APEX); assertThat(apex.getLongVersionCode()).isEqualTo(2); - assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0); + assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) + .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) .isEqualTo(ApplicationInfo.FLAG_INSTALLED); assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active"); @@ -436,7 +496,8 @@ public class StagedInstallInternalTest { assertThat(apex.getLongVersionCode()).isEqualTo(1); assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) .isEqualTo(ApplicationInfo.FLAG_SYSTEM); - assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0); + assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) + .isEqualTo(ApplicationInfo.FLAG_INSTALLED); assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex"); } } diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index f60b4d6aad1e..7e0a55ff3f3e 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -95,6 +95,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { "/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex", "/system/apex/test.rebootless_apex_v*.apex", "/data/apex/active/test.apex.rebootless*.apex", + "/system/app/TestApp/TestAppAv1.apk", TEST_VENDOR_APEX_ALLOW_LIST); } @@ -163,6 +164,25 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { } /** + * Tests app info flags are set correctly when updating a system app. + */ + @Test + public void testUpdateSystemApp() throws Exception { + // Push TestAppAv1.apk to /system + final File apkFile = mHostUtils.getTestFile(APK_A); + if (!getDevice().isAdbRoot()) { + getDevice().enableAdbRoot(); + } + getDevice().remountSystemWritable(); + assertTrue(getDevice().pushFile(apkFile, "/system/app/TestApp/" + apkFile.getName())); + getDevice().reboot(); + + runPhase("testUpdateSystemApp_InstallV2"); + getDevice().reboot(); + runPhase("testUpdateSystemApp_PostInstallV2"); + } + + /** * Tests that duplicate packages in apk-in-apex and apk should fail to install. */ @Test diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java index b7f5b15f72ac..f183a9b1d46c 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.service.voice.AlwaysOnHotwordDetector; import android.service.voice.AlwaysOnHotwordDetector.Callback; import android.service.voice.AlwaysOnHotwordDetector.EventPayload; +import android.service.voice.HotwordDetector; import android.service.voice.VoiceInteractionService; import android.util.Log; @@ -83,16 +84,24 @@ public class MainInteractionService extends VoiceInteractionService { break; case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED: Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED"); - Intent enroll = mHotwordDetector.createEnrollIntent(); - Log.i(TAG, "Need to enroll with " + enroll); + try { + Intent enroll = mHotwordDetector.createEnrollIntent(); + Log.i(TAG, "Need to enroll with " + enroll); + } catch (HotwordDetector.IllegalDetectorStateException e) { + Log.e(TAG, "createEnrollIntent failed", e); + } break; case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED: Log.i(TAG, "STATE_KEYPHRASE_ENROLLED - starting recognition"); - if (mHotwordDetector.startRecognition( - AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE)) { - Log.i(TAG, "startRecognition succeeded"); - } else { - Log.i(TAG, "startRecognition failed"); + try { + if (mHotwordDetector.startRecognition( + AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE)) { + Log.i(TAG, "startRecognition succeeded"); + } else { + Log.i(TAG, "startRecognition failed"); + } + } catch (HotwordDetector.IllegalDetectorStateException e) { + Log.e(TAG, "startRecognition failed", e); } break; } diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index bfb32854a374..0849600b0606 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -36,6 +36,7 @@ toolSources = [ cc_defaults { name: "aapt2_defaults", + cpp_std: "gnu++2b", cflags: [ "-Wall", "-Werror", diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index f47d66ea5e87..41896f622228 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -273,7 +273,7 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& printer->Indent(); for (const auto& type : package.types) { printer->Print("type "); - printer->Print(to_string(type.type)); + printer->Print(type.named_type.to_string()); if (type.id) { printer->Print(StringPrintf(" id=%02x", type.id.value())); } @@ -287,7 +287,7 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& printer->Print(" "); // Write the name without the package (this is obvious and too verbose). - printer->Print(to_string(type.type)); + printer->Print(type.named_type.to_string()); printer->Print("/"); printer->Print(entry.name); @@ -547,7 +547,7 @@ void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) const auto policy_subsection = StringPrintf(R"(policies="%s")", android::idmap2::policy::PoliciesToDebugString(overlayable_item.policies).c_str()); const auto value = - StringPrintf("%s/%s", to_string(type->type).data(), entry->name.c_str()); + StringPrintf("%s/%s", type->named_type.to_string().data(), entry->name.c_str()); items.push_back(DumpOverlayableEntry{overlayable_section, policy_subsection, value}); } } diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp index 0bb330e26e6f..df8c3b9956d0 100644 --- a/tools/aapt2/Resource.cpp +++ b/tools/aapt2/Resource.cpp @@ -139,10 +139,10 @@ ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t) { } std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s) { - auto colon = std::find(s.begin(), s.end(), ':'); + auto dot = std::find(s.begin(), s.end(), '.'); const ResourceType* parsedType; - if (colon != s.end() && colon != std::prev(s.end())) { - parsedType = ParseResourceType(s.substr(s.begin(), colon)); + if (dot != s.end() && dot != std::prev(s.end())) { + parsedType = ParseResourceType(s.substr(s.begin(), dot)); } else { parsedType = ParseResourceType(s); } diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 8d35eeec2a93..a99e4b234c6b 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -981,12 +981,14 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou return false; } - std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value()); - if (!parsed_type) { + std::optional<ResourceNamedTypeRef> maybe_parsed_type = + ParseResourceNamedType(maybe_type.value()); + if (!maybe_parsed_type) { diag->Error(DiagMessage(out_resource->source) << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">"); return false; } + auto parsed_type = maybe_parsed_type->ToResourceNamedType(); std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id"); if (!maybe_id_str) { @@ -1046,7 +1048,7 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou } ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{ - .name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()}, + .name = ResourceName{{}, parsed_type, maybe_name.value().to_string()}, .source = item_source, .comment = std::move(comment), }); diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 98cce268e213..0f5118da9408 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -43,8 +43,9 @@ namespace aapt { const char* Overlayable::kActorScheme = "overlay"; namespace { -bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) { - return lhs->type < rhs; +bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, + const ResourceNamedTypeRef& rhs) { + return lhs->named_type < rhs; } template <typename T> @@ -115,18 +116,24 @@ ResourceTablePackage* ResourceTable::FindOrCreatePackage(const android::StringPi } template <typename Func, typename Elements> -static ResourceTableType* FindTypeRunAction(ResourceType type, Elements& entries, Func action) { +static ResourceTableType* FindTypeRunAction(const ResourceNamedTypeRef& type, Elements& entries, + Func action) { const auto iter = std::lower_bound(entries.begin(), entries.end(), type, less_than_type); - const bool found = iter != entries.end() && type == (*iter)->type; + const bool found = iter != entries.end() && type == (*iter)->named_type; return action(found, iter); } -ResourceTableType* ResourceTablePackage::FindType(ResourceType type) const { +ResourceTableType* ResourceTablePackage::FindTypeWithDefaultName(const ResourceType type) const { + auto named_type = ResourceNamedTypeWithDefaultName(type); + return FindType(named_type); +} + +ResourceTableType* ResourceTablePackage::FindType(const ResourceNamedTypeRef& type) const { return FindTypeRunAction(type, types, [&](bool found, auto& iter) { return found ? iter->get() : nullptr; }); } -ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) { +ResourceTableType* ResourceTablePackage::FindOrCreateType(const ResourceNamedTypeRef& type) { return FindTypeRunAction(type, types, [&](bool found, auto& iter) { return found ? iter->get() : types.emplace(iter, new ResourceTableType(type))->get(); }); @@ -329,7 +336,7 @@ struct PackageViewComparer { struct TypeViewComparer { bool operator()(const ResourceTableTypeView& lhs, const ResourceTableTypeView& rhs) { - return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.type < rhs.type; + return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.named_type < rhs.named_type; } }; @@ -355,7 +362,8 @@ void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePacka id ? id.value().package_id() : std::optional<uint8_t>{}}; auto view_package = package_inserter.Insert(table.packages, std::move(new_package)); - ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : std::optional<uint8_t>{}}; + ResourceTableTypeView new_type{type->named_type, + id ? id.value().type_id() : std::optional<uint8_t>{}}; auto view_type = type_inserter.Insert(view_package->types, std::move(new_type)); if (visibility.level == Visibility::Level::kPublic) { @@ -420,13 +428,14 @@ ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptio // we can reuse those packages for other types that need to be extracted from this package. // `start_index` is the index of the first newly created package that can be reused. const size_t start_index = new_packages.size(); - std::map<ResourceType, size_t> type_new_package_index; + std::map<ResourceNamedType, size_t> type_new_package_index; for (auto type_it = package.types.begin(); type_it != package.types.end();) { auto& type = *type_it; - auto type_index_iter = type_new_package_index.find(type.type); + auto type_index_iter = type_new_package_index.find(type.named_type); if (type_index_iter == type_new_package_index.end()) { // First occurrence of the resource type in this package. Keep it in this package. - type_new_package_index.insert(type_index_iter, std::make_pair(type.type, start_index)); + type_new_package_index.insert(type_index_iter, + std::make_pair(type.named_type, start_index)); ++type_it; continue; } @@ -440,7 +449,7 @@ ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptio // Move the type into a new package auto& other_package = new_packages[index]; - type_new_package_index[type.type] = index + 1; + type_new_package_index[type.named_type] = index + 1; type_inserter.Insert(other_package.types, std::move(type)); type_it = package.types.erase(type_it); } @@ -473,7 +482,7 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) { } auto package = FindOrCreatePackage(res.name.package); - auto type = package->FindOrCreateType(res.name.type.type); + auto type = package->FindOrCreateType(res.name.type); auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), res.name.entry, NameEqualRange<ResourceEntry>{}); const size_t entry_count = std::distance(entry_it.first, entry_it.second); @@ -593,7 +602,7 @@ std::optional<ResourceTable::SearchResult> ResourceTable::FindResource( return {}; } - ResourceTableType* type = package->FindType(name.type.type); + ResourceTableType* type = package->FindType(name.type); if (type == nullptr) { return {}; } @@ -612,7 +621,7 @@ std::optional<ResourceTable::SearchResult> ResourceTable::FindResource(const Res return {}; } - ResourceTableType* type = package->FindType(name.type.type); + ResourceTableType* type = package->FindType(name.type); if (type == nullptr) { return {}; } @@ -633,7 +642,7 @@ bool ResourceTable::RemoveResource(const ResourceNameRef& name, ResourceId id) c return {}; } - ResourceTableType* type = package->FindType(name.type.type); + ResourceTableType* type = package->FindType(name.type); if (type == nullptr) { return {}; } @@ -655,7 +664,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const { for (const auto& pkg : packages) { ResourceTablePackage* new_pkg = new_table->FindOrCreatePackage(pkg->name); for (const auto& type : pkg->types) { - ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type); + ResourceTableType* new_type = new_pkg->FindOrCreateType(type->named_type); new_type->visibility_level = type->visibility_level; for (const auto& entry : type->entries) { diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 2e17659b0679..7aa8b0f0c8ef 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -168,7 +168,7 @@ class ResourceEntry { class ResourceTableType { public: // The logical type of resource (string, drawable, layout, etc.). - const ResourceType type; + const ResourceNamedType named_type; // Whether this type is public (and must maintain the same type ID across builds). Visibility::Level visibility_level = Visibility::Level::kUndefined; @@ -176,7 +176,9 @@ class ResourceTableType { // List of resources for this type. std::vector<std::unique_ptr<ResourceEntry>> entries; - explicit ResourceTableType(const ResourceType type) : type(type) {} + explicit ResourceTableType(const ResourceNamedTypeRef& type) + : named_type(type.ToResourceNamedType()) { + } ResourceEntry* CreateEntry(const android::StringPiece& name); ResourceEntry* FindEntry(const android::StringPiece& name) const; @@ -196,8 +198,9 @@ class ResourceTablePackage { } ResourceTablePackage() = default; - ResourceTableType* FindType(ResourceType type) const; - ResourceTableType* FindOrCreateType(ResourceType type); + ResourceTableType* FindTypeWithDefaultName(const ResourceType type) const; + ResourceTableType* FindType(const ResourceNamedTypeRef& type) const; + ResourceTableType* FindOrCreateType(const ResourceNamedTypeRef& type); private: DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage); @@ -217,7 +220,7 @@ struct ResourceTableEntryView { }; struct ResourceTableTypeView { - ResourceType type; + ResourceNamedType named_type; std::optional<uint8_t> id; Visibility::Level visibility_level = Visibility::Level::kUndefined; diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 23f6c88aad91..b4e79ca8ca08 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -40,6 +40,23 @@ using ::android::base::StringPrintf; namespace aapt { namespace ResourceUtils { +static std::optional<ResourceNamedType> ToResourceNamedType(const char16_t* type16, + const char* type, size_t type_len) { + std::optional<ResourceNamedTypeRef> parsed_type; + if (type16) { + auto converted = util::Utf16ToUtf8(StringPiece16(type16, type_len)); + parsed_type = ParseResourceNamedType(converted); + } else if (type) { + parsed_type = ParseResourceNamedType(StringPiece(type, type_len)); + } else { + return {}; + } + if (!parsed_type) { + return {}; + } + return parsed_type->ToResourceNamedType(); +} + std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name_in) { // TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2 ResourceName name_out; @@ -50,20 +67,12 @@ std::optional<ResourceName> ToResourceName(const android::ResTable::resource_nam name_out.package = util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen)); - std::optional<ResourceNamedTypeRef> type; - if (name_in.type) { - type = ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen))); - } else if (name_in.type8) { - type = ParseResourceNamedType(StringPiece(name_in.type8, name_in.typeLen)); - } else { - return {}; - } - + std::optional<ResourceNamedType> type = + ToResourceNamedType(name_in.type, name_in.name8, name_in.typeLen); if (!type) { return {}; } - - name_out.type = type->ToResourceNamedType(); + name_out.type = *type; if (name_in.name) { name_out.entry = @@ -84,21 +93,12 @@ std::optional<ResourceName> ToResourceName(const android::AssetManager2::Resourc name_out.package = std::string(name_in.package, name_in.package_len); - std::optional<ResourceNamedTypeRef> type; - if (name_in.type16) { - type = - ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len))); - } else if (name_in.type) { - type = ParseResourceNamedType(StringPiece(name_in.type, name_in.type_len)); - } else { - return {}; - } - + std::optional<ResourceNamedType> type = + ToResourceNamedType(name_in.type16, name_in.type, name_in.type_len); if (!type) { return {}; } - - name_out.type = type->ToResourceNamedType(); + name_out.type = *type; if (name_in.entry16) { name_out.entry = diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp index 2c55d1d548db..01d3c84e05ba 100644 --- a/tools/aapt2/Resource_test.cpp +++ b/tools/aapt2/Resource_test.cpp @@ -135,13 +135,13 @@ TEST(ResourceTypeTest, ParseResourceNamedType) { type = ParseResourceNamedType("layout"); EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout", ResourceType::kLayout)))); - type = ParseResourceNamedType("layout:2"); - EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:2", ResourceType::kLayout)))); + type = ParseResourceNamedType("layout.2"); + EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout.2", ResourceType::kLayout)))); - type = ParseResourceNamedType("layout:another"); - EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:another", ResourceType::kLayout)))); + type = ParseResourceNamedType("layout.another"); + EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout.another", ResourceType::kLayout)))); - type = ParseResourceNamedType("layout:"); + type = ParseResourceNamedType("layout."); EXPECT_THAT(type, Eq(std::nullopt)); type = ParseResourceNamedType("layout2"); diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index fe560180bd48..e27b9aae26fa 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -243,9 +243,9 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options, r_txt_printer.Print("private "); } - if (type->type != ResourceType::kStyleable) { + if (type->named_type.type != ResourceType::kStyleable) { r_txt_printer.Print("int "); - r_txt_printer.Print(to_string(type->type)); + r_txt_printer.Print(type->named_type.to_string()); r_txt_printer.Print(" "); r_txt_printer.Println(entry->name); } else { diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp index d9e8c921dbc5..a854146c28f6 100644 --- a/tools/aapt2/cmd/Diff.cpp +++ b/tools/aapt2/cmd/Diff.cpp @@ -105,7 +105,7 @@ static bool EmitResourceConfigValueDiff( Value* value_b = config_value_b->value.get(); if (!value_a->Equals(value_b)) { std::stringstream str_stream; - str_stream << "value " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name + str_stream << "value " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name << " config=" << config_value_a->config << " does not match:\n"; value_a->Print(&str_stream); str_stream << "\n vs \n"; @@ -128,7 +128,7 @@ static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a, auto config_value_b = entry_b.FindValue(config_value_a->config); if (!config_value_b) { std::stringstream str_stream; - str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name + str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name << " config=" << config_value_a->config; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; @@ -143,7 +143,7 @@ static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a, auto config_value_a = entry_a.FindValue(config_value_b->config); if (!config_value_a) { std::stringstream str_stream; - str_stream << "new config " << pkg_b.name << ":" << type_b.type << "/" << entry_b.name + str_stream << "new config " << pkg_b.name << ":" << type_b.named_type << "/" << entry_b.name << " config=" << config_value_b->config; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; @@ -164,13 +164,15 @@ static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, if (entry_b_iter == type_b.entries.end()) { // Type A contains a type that type B does not have. std::stringstream str_stream; - str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a_iter->name; + str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/" + << entry_a_iter->name; EmitDiffLine(apk_a->GetSource(), str_stream.str()); diff = true; } else if (entry_a_iter == type_a.entries.end()) { // Type B contains a type that type A does not have. std::stringstream str_stream; - str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/" << entry_b_iter->name; + str_stream << "new entry " << pkg_b.name << ":" << type_b.named_type << "/" + << entry_b_iter->name; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } else { @@ -178,7 +180,7 @@ static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, const auto& entry_b = *entry_b_iter; if (IsSymbolVisibilityDifferent(entry_a.visibility, entry_b.visibility)) { std::stringstream str_stream; - str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name + str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name << " has different visibility ("; if (entry_b.visibility.staged_api) { str_stream << "STAGED "; @@ -203,7 +205,7 @@ static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, } else if (IsIdDiff(entry_a.visibility.level, entry_a.id, entry_b.visibility.level, entry_b.id)) { std::stringstream str_stream; - str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name + str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name << " has different public ID ("; if (entry_b.id) { str_stream << "0x" << std::hex << entry_b.id.value(); @@ -243,13 +245,13 @@ static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, if (type_b_iter == pkg_b.types.end()) { // Type A contains a type that type B does not have. std::stringstream str_stream; - str_stream << "missing " << pkg_a.name << ":" << type_a_iter->type; + str_stream << "missing " << pkg_a.name << ":" << type_a_iter->named_type; EmitDiffLine(apk_a->GetSource(), str_stream.str()); diff = true; } else if (type_a_iter == pkg_a.types.end()) { // Type B contains a type that type A does not have. std::stringstream str_stream; - str_stream << "new type " << pkg_b.name << ":" << type_b_iter->type; + str_stream << "new type " << pkg_b.name << ":" << type_b_iter->named_type; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } else { @@ -257,7 +259,7 @@ static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, const auto& type_b = *type_b_iter; if (type_a.visibility_level != type_b.visibility_level) { std::stringstream str_stream; - str_stream << pkg_a.name << ":" << type_a.type << " has different visibility ("; + str_stream << pkg_a.name << ":" << type_a.named_type << " has different visibility ("; if (type_b.visibility_level == Visibility::Level::kPublic) { str_stream << "PUBLIC"; } else { @@ -274,7 +276,7 @@ static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, diff = true; } else if (IsIdDiff(type_a.visibility_level, type_a.id, type_b.visibility_level, type_b.id)) { std::stringstream str_stream; - str_stream << pkg_a.name << ":" << type_a.type << " has different public ID ("; + str_stream << pkg_a.name << ":" << type_a.named_type << " has different public ID ("; if (type_b.id) { str_stream << "0x" << std::hex << type_b.id.value(); } else { diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 790f2b34c58b..bd74cc7be350 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -556,7 +556,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv file_op.config = config_value->config; file_op.file_to_copy = file; - if (type->type != ResourceType::kRaw && + if (type->named_type.type != ResourceType::kRaw && (file_ref->type == ResourceFile::Type::kBinaryXml || file_ref->type == ResourceFile::Type::kProtoXml)) { std::unique_ptr<io::IData> data = file->OpenAsData(); @@ -596,7 +596,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv file_op.xml_to_flatten->file.config = config_value->config; file_op.xml_to_flatten->file.source = file_ref->GetSource(); - file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name); + file_op.xml_to_flatten->file.name = + ResourceName(pkg->name, type->named_type, entry->name); } // NOTE(adamlesinski): Explicitly construct a StringPiece here, or @@ -1009,7 +1010,7 @@ class Linker { // We have a package that is not related to the one we're building! for (const auto& type : package->types) { for (const auto& entry : type->entries) { - ResourceNameRef res_name(package->name, type->type, entry->name); + ResourceNameRef res_name(package->name, type->named_type, entry->name); for (const auto& config_value : entry->values) { // Special case the occurrence of an ID that is being generated @@ -1046,7 +1047,7 @@ class Linker { for (const auto& type : package->types) { for (const auto& entry : type->entries) { if (entry->id) { - ResourceNameRef res_name(package->name, type->type, entry->name); + ResourceNameRef res_name(package->name, type->named_type, entry->name); context_->GetDiagnostics()->Error(DiagMessage() << "resource " << res_name << " has ID " << entry->id.value() << " assigned"); return false; @@ -1057,6 +1058,83 @@ class Linker { return true; } + bool VerifyLocaleFormat(xml::XmlResource* manifest, IDiagnostics* diag) { + // Skip it if the Manifest doesn't declare the localeConfig attribute within the <application> + // element. + const xml::Element* application = manifest->root->FindChild("", "application"); + if (!application) { + return true; + } + const xml::Attribute* localeConfig = + application->FindAttribute(xml::kSchemaAndroid, "localeConfig"); + if (!localeConfig) { + return true; + } + + if (localeConfig->compiled_value) { + const auto localeconfig_reference = ValueCast<Reference>(localeConfig->compiled_value.get()); + const auto localeconfig_entry = + ResolveTableEntry(context_, &final_table_, localeconfig_reference); + if (!localeconfig_entry) { + return true; + } + + for (const auto& value : localeconfig_entry->values) { + // Load an XML file which is linked from the localeConfig attribute. + const std::string& path = value->value->GetSource().path; + std::unique_ptr<xml::XmlResource> localeConfig_xml = LoadXml(path, diag); + if (!localeConfig_xml) { + diag->Error(DiagMessage(path) << "can't load the XML"); + return false; + } + + xml::Element* localeConfig_el = xml::FindRootElement(localeConfig_xml->root.get()); + if (!localeConfig_el) { + diag->Error(DiagMessage(path) << "no root tag defined"); + return false; + } + if (localeConfig_el->name != "locale-config") { + diag->Error(DiagMessage(path) << "invalid element name: " << localeConfig_el->name + << ", expected: locale-config"); + return false; + } + + for (const xml::Element* child_el : localeConfig_el->GetChildElements()) { + if (child_el->name == "locale") { + if (const xml::Attribute* locale_name_attr = + child_el->FindAttribute(xml::kSchemaAndroid, "name")) { + const std::string& locale_name = locale_name_attr->value; + const std::string valid_name = ConvertToBCP47Tag(locale_name); + + // Start to verify the locale format + ConfigDescription config; + if (!ConfigDescription::Parse(valid_name, &config)) { + diag->Error(DiagMessage(path) << "invalid configuration: " << locale_name); + return false; + } + } else { + diag->Error(DiagMessage(path) << "the attribute android:name is not found"); + return false; + } + } else { + diag->Error(DiagMessage(path) + << "invalid element name: " << child_el->name << ", expected: locale"); + return false; + } + } + } + } + return true; + } + + std::string ConvertToBCP47Tag(const std::string& locale) { + std::string bcp47tag = "b+"; + bcp47tag += locale; + std::replace(bcp47tag.begin(), bcp47tag.end(), '-', '+'); + + return bcp47tag; + } + std::unique_ptr<IArchiveWriter> MakeArchiveWriter(const StringPiece& out) { if (options_.output_to_directory) { return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out); @@ -1939,7 +2017,7 @@ class Linker { for (auto& package : final_table_.packages) { for (auto& type : package->types) { for (auto& entry : type->entries) { - ResourceName name(package->name, type->type, entry->name); + ResourceName name(package->name, type->named_type, entry->name); // The IDs are guaranteed to exist. options_.stable_id_map[std::move(name)] = entry->id.value(); } @@ -2180,6 +2258,10 @@ class Linker { return 1; } + if (!VerifyLocaleFormat(manifest_xml.get(), context_->GetDiagnostics())) { + return 1; + }; + if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) { return 1; } diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp index 430c184ef87d..7b1236ab4a5e 100644 --- a/tools/aapt2/cmd/Link_test.cpp +++ b/tools/aapt2/cmd/Link_test.cpp @@ -22,6 +22,7 @@ #include "LoadedApk.h" #include "test/Test.h" +using android::ConfigDescription; using testing::Eq; using testing::HasSubstr; using testing::IsNull; @@ -783,4 +784,51 @@ TEST_F(LinkTest, MacroSubstitution) { EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!")); } +TEST_F(LinkTest, ParseLocaleConfig) { + StdErrDiagnostics diag; + const std::string xml_values = + R"(<locale-config xmlns:android="http://schemas.android.com/apk/res/android"> + <locale android:name="pt"/> + <locale android:name="chr"/> + <locale android:name="chr-US"/> + <locale android:name="zh-Hant"/> + <locale android:name="es-419"/> + <locale android:name="en-US"/> + <locale android:name="zh-Hans-SG"/> + </locale-config>)"; + + const std::string res = GetTestPath("test-res"); + ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locale_config.xml"), xml_values, res, &diag)); + + const std::string out_apk = GetTestPath("out.apk"); + auto link_args = LinkCommandBuilder(this) + .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build()) + .AddCompiledResDir(res, &diag) + .AddFlag("--no-auto-version") + .Build(out_apk); + ASSERT_TRUE(Link(link_args, &diag)); + + std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag); + ASSERT_THAT(apk, Ne(nullptr)); + + auto xml = apk->LoadXml("res/xml/locale_config.xml", &diag); + ASSERT_THAT(xml, NotNull()); + EXPECT_THAT(xml->root->name, Eq("locale-config")); + ASSERT_THAT(xml->root->children.size(), Eq(7)); + for (auto& node : xml->root->children) { + const xml::Element* child_el = xml::NodeCast<xml::Element>(node.get()); + ASSERT_THAT(child_el, NotNull()); + EXPECT_THAT(child_el->name, Eq("locale")); + + auto& xml_attrs = child_el->attributes; + for (auto& attr : xml_attrs) { + std::string locale = "b+"; + locale += attr.value; + std::replace(locale.begin(), locale.end(), '-', '+'); + ConfigDescription config; + ASSERT_TRUE(ConfigDescription::Parse(locale, &config)); + } + } +} + } // namespace aapt diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index caa3e60d6af1..e1370fd10ef3 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -254,7 +254,7 @@ class Optimizer { } if (file_ref->file == nullptr) { - ResourceNameRef name(pkg->name, type->type, entry->name); + ResourceNameRef name(pkg->name, type->named_type, entry->name); context_->GetDiagnostics()->Warn(DiagMessage(file_ref->GetSource()) << "file for resource " << name << " with config '" << config_value->config << "' not found"); diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp index fa816be43be3..29f9a08f9a10 100644 --- a/tools/aapt2/compile/IdAssigner.cpp +++ b/tools/aapt2/compile/IdAssigner.cpp @@ -128,7 +128,7 @@ bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) { for (auto& package : table->packages) { for (auto& type : package->types) { for (auto& entry : type->entries) { - const ResourceName name(package->name, type->type, entry->name); + const ResourceName name(package->name, type->named_type, entry->name); if (entry->id && !assigned_ids.ReserveId(name, entry->id.value(), entry->visibility, context->GetDiagnostics())) { return false; @@ -175,7 +175,7 @@ bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) { for (auto& package : table->packages) { for (auto& type : package->types) { for (auto& entry : type->entries) { - const ResourceName name(package->name, type->type, entry->name); + const ResourceName name(package->name, type->named_type, entry->name); if (entry->id) { continue; } diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp index d3575716ae4f..8911dad39470 100644 --- a/tools/aapt2/compile/IdAssigner_test.cpp +++ b/tools/aapt2/compile/IdAssigner_test.cpp @@ -191,12 +191,12 @@ TEST_F(IdAssignerTests, ExaustEntryIdsLastIdIsPublic) { for (auto& entry : type->entries) { if (!entry->id) { return ::testing::AssertionFailure() - << "resource " << ResourceNameRef(package->name, type->type, entry->name) + << "resource " << ResourceNameRef(package->name, type->named_type, entry->name) << " has no ID"; } if (!seen_ids.insert(entry->id.value()).second) { return ::testing::AssertionFailure() - << "resource " << ResourceNameRef(package->name, type->type, entry->name) + << "resource " << ResourceNameRef(package->name, type->named_type, entry->name) << " has a non-unique ID" << std::hex << entry->id.value() << std::dec; } } diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index c65c55024bab..eea7efc449b9 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -18,19 +18,19 @@ #include <algorithm> #include <map> +#include <optional> #include <string> -#include "android-base/logging.h" -#include "android-base/macros.h" -#include "android-base/stringprintf.h" -#include "androidfw/ResourceTypes.h" -#include "androidfw/TypeWrappers.h" - #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" #include "Source.h" #include "ValueVisitor.h" +#include "android-base/logging.h" +#include "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/TypeWrappers.h" #include "format/binary/ResChunkPullParser.h" #include "util/Util.h" @@ -364,7 +364,7 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, config.copyFromDtoH(type->config); const std::string type_str = util::GetString(type_pool_, type->id - 1); - const ResourceType* parsed_type = ParseResourceType(type_str); + std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(type_str); if (!parsed_type) { diag_->Warn(DiagMessage(source_) << "invalid type name '" << type_str << "' for type with ID " << type->id); diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index a9192e889c17..b42e7d0c34cd 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -360,7 +360,7 @@ class PackageFlattener { if (!FlattenValue(&flat_entry, &values_buffer)) { diag_->Error(DiagMessage() << "failed to flatten resource '" - << ResourceNameRef(package_.name, type.type, flat_entry.entry->name) + << ResourceNameRef(package_.name, type.named_type, flat_entry.entry->name) << "' for configuration '" << config << "'"); return false; } @@ -447,7 +447,7 @@ class PackageFlattener { ResourceId id = android::make_resid(package_.id.value(), type.id.value(), entry.id.value()); CHECK(seen_ids.find(id) == seen_ids.end()) << "multiple overlayable definitions found for resource " - << ResourceName(package_.name, type.type, entry.name).to_string(); + << ResourceName(package_.name, type.named_type, entry.name).to_string(); seen_ids.insert(id); // Find the overlayable chunk with the specified name @@ -592,7 +592,8 @@ class PackageFlattener { bool FlattenTypes(BigBuffer* buffer) { size_t expected_type_id = 1; for (const ResourceTableTypeView& type : package_.types) { - if (type.type == ResourceType::kStyleable || type.type == ResourceType::kMacro) { + if (type.named_type.type == ResourceType::kStyleable || + type.named_type.type == ResourceType::kMacro) { // Styleables and macros are not real resource types. continue; } @@ -606,7 +607,7 @@ class PackageFlattener { expected_type_id++; } expected_type_id++; - type_pool_.MakeRef(to_string(type.type)); + type_pool_.MakeRef(type.named_type.to_string()); if (!FlattenTypeSpec(type, type.entries, buffer)) { return false; @@ -634,7 +635,7 @@ class PackageFlattener { } uint32_t local_key_index; - ResourceName resource_name({}, type.type, entry.name); + ResourceName resource_name({}, type.named_type, entry.name); if (!collapse_key_stringpool_ || name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) { local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index(); diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index cd1c0af702cf..c73bbb51f639 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -837,4 +837,45 @@ TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) { ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table)); } +TEST_F(TableFlattenerTest, FlattenCustomResourceTypes) { + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddSimple("com.app.test:id/one", ResourceId(0x7f010000)) + .AddSimple("com.app.test:id.2/two", ResourceId(0x7f020000)) + .AddValue("com.app.test:integer/one", ResourceId(0x7f030000), + util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 10u)) + .AddValue("com.app.test:integer.1/one", ResourceId(0x7f040000), + util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u)) + .AddValue("com.app.test:integer.1/one", test::ParseConfigOrDie("v1"), + ResourceId(0x7f040000), + util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u)) + .AddString("com.app.test:layout.custom/bar", ResourceId(0x7f050000), "res/layout/bar.xml") + .Build(); + + ResTable res_table; + ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table)); + + EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f010000), {}, + Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); + + EXPECT_TRUE(Exists(&res_table, "com.app.test:id.2/two", ResourceId(0x7f020000), {}, + Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); + + EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {}, + Res_value::TYPE_INT_DEC, 10u, 0u)); + + EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000), {}, + Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION)); + + EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000), + test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u, + ResTable_config::CONFIG_VERSION)); + + std::u16string bar_path = u"res/layout/bar.xml"; + auto idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); + ASSERT_TRUE(idx.has_value()); + EXPECT_TRUE(Exists(&res_table, "com.app.test:layout.custom/bar", ResourceId(0x7f050000), {}, + Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); +} + } // namespace aapt diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index 236c38167545..82c7248f4b88 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -429,8 +429,8 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr ResourceTablePackage* pkg = out_table->FindOrCreatePackage(pb_package.package_name()); for (const pb::Type& pb_type : pb_package.type()) { - const ResourceType* res_type = ParseResourceType(pb_type.name()); - if (res_type == nullptr) { + auto res_type = ParseResourceNamedType(pb_type.name()); + if (!res_type) { std::ostringstream error; error << "unknown type '" << pb_type.name() << "'"; *out_error = error.str(); @@ -515,7 +515,7 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(), pb_entry.entry_id().id()); if (resid.is_valid()) { - id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name); + id_index[resid] = ResourceNameRef(pkg->name, type->named_type, entry->name); } for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) { diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index f3b7f758e170..bb8ea0ce4f29 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -358,7 +358,7 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table if (type.id) { pb_type->mutable_type_id()->set_id(type.id.value()); } - pb_type->set_name(to_string(type.type).to_string()); + pb_type->set_name(type.named_type.to_string()); // hardcoded string uses characters which make it an invalid resource name static const char* obfuscated_resource_name = "0_resource_name_obfuscated"; @@ -367,7 +367,7 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table if (entry.id) { pb_entry->mutable_entry_id()->set_id(entry.id.value()); } - ResourceName resource_name({}, type.type, entry.name); + ResourceName resource_name({}, type.named_type, entry.name); if (options.collapse_key_stringpool && options.name_collapse_exemptions.find(resource_name) == options.name_collapse_exemptions.end()) { diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index d1d72e012b31..0247021f1f8a 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -951,4 +951,76 @@ TEST(ProtoSerializeTest, StagedId) { EXPECT_THAT(result.value().entry->staged_id.value().id, Eq(ResourceId(0x01ff0001))); } +TEST(ProtoSerializeTest, CustomResourceTypes) { + const uint32_t id_one_id = 0x7f020000; + const uint32_t id_2_two_id = 0x7f030000; + const uint32_t integer_three_id = 0x7f030000; + const uint32_t integer_1_four_id = 0x7f030000; + const uint32_t layout_bar_id = 0x7f050000; + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddSimple("com.app.test:id/one", ResourceId(id_one_id)) + .AddSimple("com.app.test:id.2/two", ResourceId(id_2_two_id)) + .AddValue( + "com.app.test:integer/one", ResourceId(integer_three_id), + util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 10u)) + .AddValue( + "com.app.test:integer.1/one", ResourceId(integer_1_four_id), + util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 1u)) + .AddValue( + "com.app.test:integer.1/one", test::ParseConfigOrDie("v1"), + ResourceId(integer_1_four_id), + util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 2u)) + .AddFileReference("com.app.test:layout.custom/bar", ResourceId(layout_bar_id), + "res/layout/bar.xml") + .Build(); + + test::TestFile file_a("res/layout/bar.xml"); + MockFileCollection files; + EXPECT_CALL(files, FindFile(Eq("res/layout/bar.xml"))).WillRepeatedly(::testing::Return(&file_a)); + + ResourceTable new_table; + pb::ResourceTable pb_table; + std::string error; + SerializeTableToPb(*table, &pb_table, context->GetDiagnostics()); + DeserializeTableFromPb(pb_table, &files, &new_table, &error); + ASSERT_THAT(error, IsEmpty()); + + auto bp = test::GetValueForConfigAndProduct<BinaryPrimitive>( + &new_table, "com.app.test:integer.1/one", ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("1")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer.1/one", + test::ParseConfigOrDie("v1"), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("2")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer/one", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("10")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer/one", + test::ParseConfigOrDie("v1"), ""); + ASSERT_THAT(bp, IsNull()); + + auto id = test::GetValueForConfigAndProduct<Id>(&new_table, "com.app.test:id/one", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(id, NotNull()); + + id = test::GetValueForConfigAndProduct<Id>(&new_table, "com.app.test:id.2/two", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(id, NotNull()); + + auto custom_layout = test::GetValueForConfigAndProduct<FileReference>( + &new_table, "com.app.test:layout.custom/bar", ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(custom_layout, NotNull()); + EXPECT_THAT(*(custom_layout->path), Eq("res/layout/bar.xml")); +} + } // namespace aapt diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp index a963d9893f2f..a25ca22c288d 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -548,10 +548,11 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate } // We need to make sure we hide the fact that we are generating kAttrPrivate attributes. - const ResourceNameRef resource_name( - package_name_to_generate, - type.type == ResourceType::kAttrPrivate ? ResourceType::kAttr : type.type, - unmangled_name.value()); + const auto target_type = type.named_type.type == ResourceType::kAttrPrivate + ? ResourceNamedTypeWithDefaultName(ResourceType::kAttr) + : type.named_type; + const ResourceNameRef resource_name(package_name_to_generate, target_type, + unmangled_name.value()); // Check to see if the unmangled name is a valid Java name (not a keyword). if (!IsValidSymbol(unmangled_name.value())) { @@ -616,7 +617,8 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, for (const auto& package : table_->packages) { for (const auto& type : package->types) { - if (type->type == ResourceType::kAttrPrivate || type->type == ResourceType::kMacro) { + if (type->named_type.type == ResourceType::kAttrPrivate || + type->named_type.type == ResourceType::kMacro) { // We generate kAttrPrivate as part of the kAttr type, so skip them here. // Macros are not actual resources, so skip them as well. continue; @@ -628,7 +630,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::unique_ptr<ClassDefinition> class_def; if (out != nullptr) { class_def = util::make_unique<ClassDefinition>( - to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty); + to_string(type->named_type.type), ClassQualifier::kStatic, force_creation_if_empty); } if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(), @@ -636,9 +638,10 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, return false; } - if (type->type == ResourceType::kAttr) { + if (type->named_type.type == ResourceType::kAttr) { // Also include private attributes in this same class. - if (const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate)) { + if (const ResourceTableType* priv_type = + package->FindTypeWithDefaultName(ResourceType::kAttrPrivate)) { if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(), rewrite_method.get(), r_txt_printer.get())) { return false; @@ -646,7 +649,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, } } - if (out != nullptr && type->type == ResourceType::kStyleable && is_public) { + if (out != nullptr && type->named_type.type == ResourceType::kStyleable && is_public) { // When generating a public R class, we don't want Styleable to be part // of the API. It is only emitted for documentation purposes. class_def->GetCommentBuilder()->AppendComment("@doconly"); diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp index e53e22070b62..80a46d553960 100644 --- a/tools/aapt2/java/ProguardRules.cpp +++ b/tools/aapt2/java/ProguardRules.cpp @@ -517,7 +517,7 @@ bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table for (auto& type : pkg->types) { for (auto& entry : type->entries) { for (auto& config_value : entry->values) { - ResourceName from(pkg->name, type->type, entry->name); + ResourceName from(pkg->name, type->named_type, entry->name); ReferenceVisitor visitor(context, from, keep_set); config_value->value->Accept(&visitor); } diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp index 328ac97090a8..3dbd7e613a3e 100644 --- a/tools/aapt2/link/AutoVersioner.cpp +++ b/tools/aapt2/link/AutoVersioner.cpp @@ -75,7 +75,7 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) { CloningValueTransformer cloner(&table->string_pool); for (auto& package : table->packages) { for (auto& type : package->types) { - if (type->type != ResourceType::kStyle) { + if (type->named_type.type != ResourceType::kStyle) { continue; } diff --git a/tools/aapt2/link/NoDefaultResourceRemover.cpp b/tools/aapt2/link/NoDefaultResourceRemover.cpp index 05990de6a9b3..ab3c04e59741 100644 --- a/tools/aapt2/link/NoDefaultResourceRemover.cpp +++ b/tools/aapt2/link/NoDefaultResourceRemover.cpp @@ -76,7 +76,7 @@ bool NoDefaultResourceRemover::Consume(IAaptContext* context, ResourceTable* tab }); for (auto iter = remove_iter; iter != end_iter; ++iter) { - const ResourceName name(pkg->name, type->type, (*iter)->name); + const ResourceName name(pkg->name, type->named_type, (*iter)->name); IDiagnostics* diag = context->GetDiagnostics(); diag->Warn(DiagMessage() << "removing resource " << name << " without required default value"); diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp index 675b02a7e161..8c6c743dfff0 100644 --- a/tools/aapt2/link/PrivateAttributeMover.cpp +++ b/tools/aapt2/link/PrivateAttributeMover.cpp @@ -57,7 +57,7 @@ OutputIterator move_if(InputContainer& input_container, OutputIterator result, P bool PrivateAttributeMover::Consume(IAaptContext* context, ResourceTable* table) { for (auto& package : table->packages) { - ResourceTableType* type = package->FindType(ResourceType::kAttr); + ResourceTableType* type = package->FindTypeWithDefaultName(ResourceType::kAttr); if (!type) { continue; } @@ -80,7 +80,8 @@ bool PrivateAttributeMover::Consume(IAaptContext* context, ResourceTable* table) continue; } - ResourceTableType* priv_attr_type = package->FindOrCreateType(ResourceType::kAttrPrivate); + auto attr_private_type = ResourceNamedTypeWithDefaultName(ResourceType::kAttrPrivate); + ResourceTableType* priv_attr_type = package->FindOrCreateType(attr_private_type); CHECK(priv_attr_type->entries.empty()); priv_attr_type->entries = std::move(private_attr_entries); } diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp index 168234b36e4a..32335b7f5a9f 100644 --- a/tools/aapt2/link/PrivateAttributeMover_test.cpp +++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp @@ -41,13 +41,13 @@ TEST(PrivateAttributeMoverTest, MovePrivateAttributes) { ResourceTablePackage* package = table->FindPackage("android"); ASSERT_NE(package, nullptr); - ResourceTableType* type = package->FindType(ResourceType::kAttr); + ResourceTableType* type = package->FindTypeWithDefaultName(ResourceType::kAttr); ASSERT_NE(type, nullptr); ASSERT_EQ(type->entries.size(), 2u); EXPECT_NE(type->FindEntry("publicA"), nullptr); EXPECT_NE(type->FindEntry("publicB"), nullptr); - type = package->FindType(ResourceType::kAttrPrivate); + type = package->FindTypeWithDefaultName(ResourceType::kAttrPrivate); ASSERT_NE(type, nullptr); ASSERT_EQ(type->entries.size(), 2u); EXPECT_NE(type->FindEntry("privateA"), nullptr); @@ -68,11 +68,11 @@ TEST(PrivateAttributeMoverTest, LeavePrivateAttributesWhenNoPublicAttributesDefi ResourceTablePackage* package = table->FindPackage("android"); ASSERT_NE(package, nullptr); - ResourceTableType* type = package->FindType(ResourceType::kAttr); + ResourceTableType* type = package->FindTypeWithDefaultName(ResourceType::kAttr); ASSERT_NE(type, nullptr); ASSERT_EQ(type->entries.size(), 2u); - type = package->FindType(ResourceType::kAttrPrivate); + type = package->FindTypeWithDefaultName(ResourceType::kAttrPrivate); ASSERT_EQ(type, nullptr); } @@ -87,12 +87,12 @@ TEST(PrivateAttributeMoverTest, DoNotCreatePrivateAttrsIfNoneExist) { ResourceTablePackage* package = table->FindPackage("android"); ASSERT_NE(nullptr, package); - ASSERT_EQ(nullptr, package->FindType(ResourceType::kAttrPrivate)); + ASSERT_EQ(nullptr, package->FindTypeWithDefaultName(ResourceType::kAttrPrivate)); PrivateAttributeMover mover; ASSERT_TRUE(mover.Consume(context.get(), table.get())); - ASSERT_EQ(nullptr, package->FindType(ResourceType::kAttrPrivate)); + ASSERT_EQ(nullptr, package->FindTypeWithDefaultName(ResourceType::kAttrPrivate)); } } // namespace aapt diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp index 793740af3021..0c54a739d347 100644 --- a/tools/aapt2/link/ProductFilter.cpp +++ b/tools/aapt2/link/ProductFilter.cpp @@ -98,7 +98,7 @@ bool ProductFilter::Consume(IAaptContext* context, ResourceTable* table) { // End of the array, or we saw a different config, // so this must be the end of a range of products. // Select the product to keep from the set of products defined. - ResourceNameRef name(pkg->name, type->type, entry->name); + ResourceNameRef name(pkg->name, type->named_type, entry->name); auto value_to_keep = SelectProductToKeep( name, start_range_iter, iter, context->GetDiagnostics()); if (value_to_keep == iter) { diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 5372cf243951..d1fbffa1debd 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -468,7 +468,7 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) { for (auto& type : package->types) { for (auto& entry : type->entries) { // First, unmangle the name if necessary. - ResourceName name(package->name, type->type, entry->name); + ResourceName name(package->name, type->named_type, entry->name); NameMangler::Unmangle(&name.entry, &name.package); // Symbol state information may be lost if there is no value for the resource. diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index d78f0ac17f67..caaaba63931f 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -235,7 +235,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package, bool error = false; for (auto& src_type : src_package->types) { - ResourceTableType* dst_type = main_package_->FindOrCreateType(src_type->type); + ResourceTableType* dst_type = main_package_->FindOrCreateType(src_type->named_type); if (!MergeType(context_, src, dst_type, src_type.get())) { error = true; continue; @@ -254,7 +254,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package, dst_entry = dst_type->FindEntry(entry_name); } - const ResourceNameRef res_name(src_package->name, src_type->type, src_entry->name); + const ResourceNameRef res_name(src_package->name, src_type->named_type, src_entry->name); if (!dst_entry) { context_->GetDiagnostics()->Error(DiagMessage(src) @@ -349,7 +349,7 @@ bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFi file_ref->file = file; ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package); - pkg->FindOrCreateType(file_desc.name.type.type) + pkg->FindOrCreateType(file_desc.name.type) ->FindOrCreateEntry(file_desc.name.entry) ->FindOrCreateValue(file_desc.config, {}) ->value = std::move(file_ref); diff --git a/tools/aapt2/optimize/ResourceFilter.cpp b/tools/aapt2/optimize/ResourceFilter.cpp index 08c045bf68f7..db84b66ecd2d 100644 --- a/tools/aapt2/optimize/ResourceFilter.cpp +++ b/tools/aapt2/optimize/ResourceFilter.cpp @@ -28,7 +28,7 @@ bool ResourceFilter::Consume(IAaptContext* context, ResourceTable* table) { for (auto& package : table->packages) { for (auto& type : package->types) { for (auto it = type->entries.begin(); it != type->entries.end(); ) { - ResourceName resource = ResourceName({}, type->type, (*it)->name); + ResourceName resource = ResourceName({}, type->named_type, (*it)->name); if (exclude_list_.find(resource) != exclude_list_.end()) { it = type->entries.erase(it); } else { diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp index 116b2ab9aa98..85d150f16b26 100644 --- a/tools/aapt2/split/TableSplitter.cpp +++ b/tools/aapt2/split/TableSplitter.cpp @@ -189,7 +189,7 @@ void TableSplitter::SplitTable(ResourceTable* original_table) { } for (auto& type : pkg->types) { - if (type->type == ResourceType::kMipmap) { + if (type->named_type.type == ResourceType::kMipmap) { // Always keep mipmaps. continue; } @@ -241,7 +241,7 @@ void TableSplitter::SplitTable(ResourceTable* original_table) { // Create the same resource structure in the split. We do this lazily because we might // not have actual values for each type/entry. ResourceTablePackage* split_pkg = split_table->FindPackage(pkg->name); - ResourceTableType* split_type = split_pkg->FindOrCreateType(type->type); + ResourceTableType* split_type = split_pkg->FindOrCreateType(type->named_type); split_type->visibility_level = type->visibility_level; ResourceEntry* split_entry = split_type->FindOrCreateEntry(entry->name); diff --git a/tools/lint/README.md b/tools/lint/README.md index b534b62cb395..c674d36431b7 100644 --- a/tools/lint/README.md +++ b/tools/lint/README.md @@ -78,6 +78,7 @@ adding `cmd.Flag("--nowarn")` and running lint again. ## Documentation - [Android Lint Docs](https://googlesamples.github.io/android-custom-lint-rules/) +- [Lint Check Unit Testing](https://googlesamples.github.io/android-custom-lint-rules/api-guide/unit-testing.md.html) - [Android Lint source files](https://source.corp.google.com/studio-main/tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/) - [PSI source files](https://github.com/JetBrains/intellij-community/tree/master/java/java-psi-api/src/com/intellij/psi) - [UAST source files](https://upsource.jetbrains.com/idea-ce/structure/idea-ce-7b9b8cc138bbd90aec26433f82cd2c6838694003/uast/uast-common/src/org/jetbrains/uast) diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt index a6fd9bba6192..859961a2a079 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt @@ -19,6 +19,7 @@ package com.google.android.lint import com.android.tools.lint.client.api.IssueRegistry import com.android.tools.lint.client.api.Vendor import com.android.tools.lint.detector.api.CURRENT_API +import com.google.android.lint.parcel.SaferParcelChecker import com.google.auto.service.AutoService @AutoService(IssueRegistry::class) @@ -33,7 +34,8 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() { CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY, CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED, EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION, - EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION + EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION, + SaferParcelChecker.ISSUE_UNSAFE_API_USAGE, ) override val api: Int diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt index 8011b36c9a8f..9f216189ad62 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt @@ -16,6 +16,7 @@ package com.google.android.lint +import com.android.tools.lint.client.api.UElementHandler import com.android.tools.lint.detector.api.AnnotationInfo import com.android.tools.lint.detector.api.AnnotationOrigin import com.android.tools.lint.detector.api.AnnotationUsageInfo @@ -32,22 +33,39 @@ import com.android.tools.lint.detector.api.SourceCodeScanner import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClass import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UAnnotation import org.jetbrains.uast.UElement +import org.jetbrains.uast.UClass +import org.jetbrains.uast.UMethod /** * Lint Detector that ensures that any method overriding a method annotated * with @EnforcePermission is also annotated with the exact same annotation. * The intent is to surface the effective permission checks to the service * implementations. + * + * This is done with 2 mechanisms: + * 1. Visit any annotation usage, to ensure that any derived class will have + * the correct annotation on each methods. This is for the top to bottom + * propagation. + * 2. Visit any annotation, to ensure that if a method is annotated, it has + * its ancestor also annotated. This is to avoid having an annotation on a + * Java method without the corresponding annotation on the AIDL interface. */ class EnforcePermissionDetector : Detector(), SourceCodeScanner { val ENFORCE_PERMISSION = "android.annotation.EnforcePermission" + val BINDER_CLASS = "android.os.Binder" + val JAVA_OBJECT = "java.lang.Object" override fun applicableAnnotations(): List<String> { return listOf(ENFORCE_PERMISSION) } + override fun getApplicableUastTypes(): List<Class<out UElement>> { + return listOf(UAnnotation::class.java) + } + private fun areAnnotationsEquivalent( context: JavaContext, anno1: PsiAnnotation, @@ -74,6 +92,73 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { return true } + private fun compareMethods( + context: JavaContext, + element: UElement, + overridingMethod: PsiMethod, + overriddenMethod: PsiMethod, + checkEquivalence: Boolean = true + ) { + val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION) + val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION) + val location = context.getLocation(element) + val overridingClass = overridingMethod.parent as PsiClass + val overriddenClass = overriddenMethod.parent as PsiClass + val overridingName = "${overridingClass.name}.${overridingMethod.name}" + val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}" + if (overridingAnnotation == null) { + val msg = "The method $overridingName overrides the method $overriddenName which " + + "is annotated with @EnforcePermission. The same annotation must be used " + + "on $overridingName" + context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg) + } else if (overriddenAnnotation == null) { + val msg = "The method $overridingName overrides the method $overriddenName which " + + "is not annotated with @EnforcePermission. The same annotation must be " + + "used on $overriddenName. Did you forget to annotate the AIDL definition?" + context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg) + } else if (checkEquivalence && !areAnnotationsEquivalent( + context, overridingAnnotation, overriddenAnnotation)) { + val msg = "The method $overridingName is annotated with " + + "${overridingAnnotation.text} which differs from the overridden " + + "method $overriddenName: ${overriddenAnnotation.text}. The same " + + "annotation must be used for both methods." + context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg) + } + } + + private fun compareClasses( + context: JavaContext, + element: UElement, + newClass: PsiClass, + extendedClass: PsiClass, + checkEquivalence: Boolean = true + ) { + val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION) + val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION) + + val location = context.getLocation(element) + val newClassName = newClass.qualifiedName + val extendedClassName = extendedClass.qualifiedName + if (newAnnotation == null) { + val msg = "The class $newClassName extends the class $extendedClassName which " + + "is annotated with @EnforcePermission. The same annotation must be used " + + "on $newClassName." + context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg) + } else if (extendedAnnotation == null) { + val msg = "The class $newClassName extends the class $extendedClassName which " + + "is not annotated with @EnforcePermission. The same annotation must be used " + + "on $extendedClassName. Did you forget to annotate the AIDL definition?" + context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg) + } else if (checkEquivalence && !areAnnotationsEquivalent( + context, newAnnotation, extendedAnnotation)) { + val msg = "The class $newClassName is annotated with ${newAnnotation.text} " + + "which differs from the parent class $extendedClassName: " + + "${extendedAnnotation.text}. The same annotation must be used for " + + "both classes." + context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg) + } + } + override fun visitAnnotationUsage( context: JavaContext, element: UElement, @@ -83,48 +168,42 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { if (usageInfo.type == AnnotationUsageType.EXTENDS) { val newClass = element.sourcePsi?.parent?.parent as PsiClass val extendedClass: PsiClass = usageInfo.referenced as PsiClass - val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION) - val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)!! - - val location = context.getLocation(element) - val newClassName = newClass.qualifiedName - val extendedClassName = extendedClass.qualifiedName - if (newAnnotation == null) { - val msg = "The class $newClassName extends the class $extendedClassName which " + - "is annotated with @EnforcePermission. The same annotation must be used " + - "on $newClassName." - context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg) - } else if (!areAnnotationsEquivalent(context, newAnnotation, extendedAnnotation)) { - val msg = "The class $newClassName is annotated with ${newAnnotation.text} " + - "which differs from the parent class $extendedClassName: " + - "${extendedAnnotation.text}. The same annotation must be used for " + - "both classes." - context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg) - } + compareClasses(context, element, newClass, extendedClass) } else if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE && annotationInfo.origin == AnnotationOrigin.METHOD) { val overridingMethod = element.sourcePsi as PsiMethod val overriddenMethod = usageInfo.referenced as PsiMethod - val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION) - val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)!! - - val location = context.getLocation(element) - val overridingClass = overridingMethod.parent as PsiClass - val overriddenClass = overriddenMethod.parent as PsiClass - val overridingName = "${overridingClass.name}.${overridingMethod.name}" - val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}" - if (overridingAnnotation == null) { - val msg = "The method $overridingName overrides the method $overriddenName which " + - "is annotated with @EnforcePermission. The same annotation must be used " + - "on $overridingName" - context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg) - } else if (!areAnnotationsEquivalent( - context, overridingAnnotation, overriddenAnnotation)) { - val msg = "The method $overridingName is annotated with " + - "${overridingAnnotation.text} which differs from the overridden " + - "method $overriddenName: ${overriddenAnnotation.text}. The same " + - "annotation must be used for both methods." - context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg) + compareMethods(context, element, overridingMethod, overriddenMethod) + } + } + + override fun createUastHandler(context: JavaContext): UElementHandler { + return object : UElementHandler() { + override fun visitAnnotation(node: UAnnotation) { + if (node.qualifiedName != ENFORCE_PERMISSION) { + return + } + val method = node.uastParent as? UMethod + val klass = node.uastParent as? UClass + if (klass != null) { + val newClass = klass as PsiClass + val extendedClass = newClass.getSuperClass() + if (extendedClass != null && extendedClass.qualifiedName != JAVA_OBJECT) { + // The equivalence check can be skipped, if both classes are + // annotated, it will be verified by visitAnnotationUsage. + compareClasses(context, klass, newClass, + extendedClass, checkEquivalence = false) + } + } else if (method != null) { + val overridingMethod = method as PsiMethod + val parents = overridingMethod.findSuperMethods() + for (overriddenMethod in parents) { + // The equivalence check can be skipped, if both methods are + // annotated, it will be verified by visitAnnotationUsage. + compareMethods(context, method, overridingMethod, + overriddenMethod, checkEquivalence = false) + } + } } } } diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt new file mode 100644 index 000000000000..cc2ab19c97f1 --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2022 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.google.android.lint.parcel + +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.LintFix +import com.android.tools.lint.detector.api.Location +import com.intellij.psi.PsiCallExpression +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiIntersectionType +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypeParameter +import com.intellij.psi.PsiWildcardType +import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.UExpression +import org.jetbrains.uast.UVariable + +/** + * Subclass this class and override {@link #getBoundingClass} to report an unsafe Parcel API issue + * with a fix that migrates towards the new safer API by appending an argument in the form of + * {@code com.package.ItemType.class} coming from the result of the overridden method. + */ +abstract class CallMigrator( + val method: Method, + private val rejects: Set<String> = emptySet(), +) { + open fun report(context: JavaContext, call: UCallExpression, method: PsiMethod) { + val location = context.getLocation(call) + val itemType = getBoundingClass(context, call, method) + val fix = (itemType as? PsiClassType)?.let { type -> + getParcelFix(location, this.method.name, getArgumentSuffix(type)) + } + val message = "Unsafe `Parcel.${this.method.name}()` API usage" + context.report(SaferParcelChecker.ISSUE_UNSAFE_API_USAGE, call, location, message, fix) + } + + protected open fun getArgumentSuffix(type: PsiClassType) = + ", ${type.rawType().canonicalText}.class" + + protected open fun getBoundingClass( + context: JavaContext, + call: UCallExpression, + method: PsiMethod, + ): PsiType? = null + + protected fun getItemType(type: PsiType, container: String): PsiClassType? { + val supers = getParentTypes(type).mapNotNull { it as? PsiClassType } + val containerType = supers.firstOrNull { it.rawType().canonicalText == container } + ?: return null + val itemType = containerType.parameters.getOrNull(0) ?: return null + // TODO: Expand to other types, see PsiTypeVisitor + return when (itemType) { + is PsiClassType -> itemType + is PsiWildcardType -> itemType.bound as PsiClassType + else -> null + } + } + + /** + * Tries to obtain the type expected by the "receiving" end given a certain {@link UExpression}. + * + * This could be an assignment, an argument passed to a method call, to a constructor call, a + * type cast, etc. If no receiving end is found, the type of the UExpression itself is returned. + */ + protected fun getReceivingType(expression: UExpression): PsiType? { + val parent = expression.uastParent + val type = when (parent) { + is UCallExpression -> { + val i = parent.valueArguments.indexOf(expression) + val psiCall = parent.sourcePsi as? PsiCallExpression ?: return null + val typeSubstitutor = psiCall.resolveMethodGenerics().substitutor + val method = psiCall.resolveMethod()!! + method.getSignature(typeSubstitutor).parameterTypes[i] + } + is UVariable -> parent.type + is UExpression -> parent.getExpressionType() + else -> null + } + return filter(type ?: expression.getExpressionType()) + } + + private fun filter(type: PsiType?): PsiType? { + // It's important that PsiIntersectionType case is above the one that check the type in + // rejects, because for intersect types, the canonicalText is one of the terms. + if (type is PsiIntersectionType) { + return type.conjuncts.mapNotNull(this::filter).firstOrNull() + } + if (type == null || type.canonicalText in rejects) { + return null + } + if (type is PsiClassType && type.resolve() is PsiTypeParameter) { + return null + } + return type + } + + private fun getParentTypes(type: PsiType): Set<PsiType> = + type.superTypes.flatMap(::getParentTypes).toSet() + type + + protected fun getParcelFix(location: Location, method: String, arguments: String) = + LintFix + .create() + .name("Migrate to safer Parcel.$method() API") + .replace() + .range(location) + .pattern("$method\\s*\\(((?:.|\\n)*)\\)") + .with("\\k<1>$arguments") + .autoFix() + .build() +} + +/** + * This class derives the type to be appended by inferring the generic type of the {@code container} + * type (eg. "java.util.List") of the {@code argument}-th argument. + */ +class ContainerArgumentMigrator( + method: Method, + private val argument: Int, + private val container: String, + rejects: Set<String> = emptySet(), +) : CallMigrator(method, rejects) { + override fun getBoundingClass( + context: JavaContext, call: UCallExpression, method: PsiMethod + ): PsiType? { + val firstParamType = call.valueArguments[argument].getExpressionType() ?: return null + return getItemType(firstParamType, container)!! + } + + /** + * We need to insert a casting construct in the class parameter. For example: + * (Class<Foo<Bar>>) (Class<?>) Foo.class. + * This is needed for when the arguments of the conflict (eg. when there is List<Foo<Bar>> and + * class type is Class<Foo?). + */ + override fun getArgumentSuffix(type: PsiClassType): String { + if (type.parameters.isNotEmpty()) { + val rawType = type.rawType() + return ", (Class<${type.canonicalText}>) (Class<?>) ${rawType.canonicalText}.class" + } + return super.getArgumentSuffix(type) + } +} + +/** + * This class derives the type to be appended by inferring the generic type of the {@code container} + * type (eg. "java.util.List") of the return type of the method. + */ +class ContainerReturnMigrator( + method: Method, + private val container: String, + rejects: Set<String> = emptySet(), +) : CallMigrator(method, rejects) { + override fun getBoundingClass( + context: JavaContext, call: UCallExpression, method: PsiMethod + ): PsiType? { + val type = getReceivingType(call.uastParent as UExpression) ?: return null + return getItemType(type, container) + } +} + +/** + * This class derives the type to be appended by inferring the expected type for the method result. + */ +class ReturnMigrator( + method: Method, + rejects: Set<String> = emptySet(), +) : CallMigrator(method, rejects) { + override fun getBoundingClass( + context: JavaContext, call: UCallExpression, method: PsiMethod + ): PsiType? { + return getReceivingType(call.uastParent as UExpression) + } +} + +/** + * This class appends the class loader and the class object by deriving the type from the method + * result. + */ +class ReturnMigratorWithClassLoader( + method: Method, + rejects: Set<String> = emptySet(), +) : CallMigrator(method, rejects) { + override fun getBoundingClass( + context: JavaContext, call: UCallExpression, method: PsiMethod + ): PsiType? { + return getReceivingType(call.uastParent as UExpression) + } + + override fun getArgumentSuffix(type: PsiClassType): String = + "${type.rawType().canonicalText}.class.getClassLoader(), " + + "${type.rawType().canonicalText}.class" + +}
\ No newline at end of file diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt new file mode 100644 index 000000000000..c032fa29f254 --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 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.google.android.lint.parcel + +data class Method( + val params: List<String>, + val clazz: String, + val name: String, + val parameters: List<String> +) { + constructor( + clazz: String, + name: String, + parameters: List<String> + ) : this( + listOf(), clazz, name, parameters + ) + + val signature: String + get() { + val prefix = if (params.isEmpty()) "" else "${params.joinToString(", ", "<", ">")} " + return "$prefix$clazz.$name(${parameters.joinToString()})" + } +}
\ No newline at end of file diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt new file mode 100644 index 000000000000..89dbcaeac3a7 --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 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.google.android.lint.parcel + +import com.android.tools.lint.detector.api.* +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiSubstitutor +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypeParameter +import org.jetbrains.uast.UCallExpression +import java.util.* + +class SaferParcelChecker : Detector(), SourceCodeScanner { + override fun getApplicableMethodNames(): List<String> = + MIGRATORS + .map(CallMigrator::method) + .map(Method::name) + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + if (!isAtLeastT(context)) return + val signature = getSignature(method) + val migrator = MIGRATORS.firstOrNull { it.method.signature == signature } ?: return + migrator.report(context, node, method) + } + + private fun getSignature(method: PsiMethod): String { + val name = UastLintUtils.getQualifiedName(method) + val signature = method.getSignature(PsiSubstitutor.EMPTY) + val parameters = + signature.parameterTypes.joinToString(transform = PsiType::getCanonicalText) + val types = signature.typeParameters.map(PsiTypeParameter::getName) + val prefix = if (types.isEmpty()) "" else types.joinToString(", ", "<", ">") + " " + return "$prefix$name($parameters)" + } + + /** Taken from androidx-main:core/core/src/main/java/androidx/core/os/BuildCompat.java */ + private fun isAtLeastT(context: Context): Boolean { + val project = if (context.isGlobalAnalysis()) context.mainProject else context.project + return project.isAndroidProject + && project.minSdkVersion.featureLevel >= 32 + && isAtLeastPreReleaseCodename("Tiramisu", project.minSdkVersion.codename) + } + + /** Taken from androidx-main:core/core/src/main/java/androidx/core/os/BuildCompat.java */ + private fun isAtLeastPreReleaseCodename(min: String, actual: String): Boolean { + if (actual == "REL") return false + return actual.uppercase(Locale.ROOT) >= min.uppercase(Locale.ROOT) + } + + companion object { + @JvmField + val ISSUE_UNSAFE_API_USAGE: Issue = Issue.create( + id = "UnsafeParcelApi", + briefDescription = "Use of unsafe Parcel API", + explanation = """ + You are using a deprecated Parcel API that doesn't accept the expected class as\ + a parameter. This means that unexpected classes could be instantiated and\ + unexpected code executed. + + Please migrate to the safer alternative that takes an extra Class<T> parameter. + """, + category = Category.SECURITY, + priority = 8, + severity = Severity.WARNING, + + implementation = Implementation( + SaferParcelChecker::class.java, + Scope.JAVA_FILE_SCOPE + ) + ) + + private val METHOD_READ_SERIALIZABLE = Method("android.os.Parcel", "readSerializable", listOf()) + private val METHOD_READ_ARRAY_LIST = Method("android.os.Parcel", "readArrayList", listOf("java.lang.ClassLoader")) + private val METHOD_READ_LIST = Method("android.os.Parcel", "readList", listOf("java.util.List", "java.lang.ClassLoader")) + private val METHOD_READ_PARCELABLE = Method(listOf("T"), "android.os.Parcel", "readParcelable", listOf("java.lang.ClassLoader")) + private val METHOD_READ_PARCELABLE_LIST = Method(listOf("T"), "android.os.Parcel", "readParcelableList", listOf("java.util.List<T>", "java.lang.ClassLoader")) + private val METHOD_READ_SPARSE_ARRAY = Method(listOf("T"), "android.os.Parcel", "readSparseArray", listOf("java.lang.ClassLoader")) + + // TODO: Write migrators for methods below + private val METHOD_READ_ARRAY = Method("android.os.Parcel", "readArray", listOf("java.lang.ClassLoader")) + private val METHOD_READ_PARCELABLE_ARRAY = Method("android.os.Parcel", "readParcelableArray", listOf("java.lang.ClassLoader")) + private val METHOD_READ_PARCELABLE_CREATOR = Method("android.os.Parcel", "readParcelableCreator", listOf("java.lang.ClassLoader")) + + private val MIGRATORS = listOf( + ReturnMigrator(METHOD_READ_PARCELABLE, setOf("android.os.Parcelable")), + ContainerArgumentMigrator(METHOD_READ_LIST, 0, "java.util.List"), + ContainerReturnMigrator(METHOD_READ_ARRAY_LIST, "java.util.Collection"), + ContainerReturnMigrator(METHOD_READ_SPARSE_ARRAY, "android.util.SparseArray"), + ContainerArgumentMigrator(METHOD_READ_PARCELABLE_LIST, 0, "java.util.List"), + ReturnMigratorWithClassLoader(METHOD_READ_SERIALIZABLE), + ) + } +}
\ No newline at end of file diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt index f5f4ebee24e0..2cfc3fbcefcb 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt +++ b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt @@ -147,14 +147,57 @@ annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnota 1 errors, 0 warnings""".addLineContinuation()) } + fun testDetectIssuesExtraAnnotationMethod() { + lint().files(java( + """ + package test.pkg; + public class TestClass7 extends IBar.Stub { + @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) + public void testMethod() {} + } + """).indented(), + *stubs + ) + .run() + .expect("""src/test/pkg/TestClass7.java:4: Error: The method TestClass7.testMethod \ +overrides the method Stub.testMethod which is not annotated with @EnforcePermission. The same \ +annotation must be used on Stub.testMethod. Did you forget to annotate the AIDL definition? \ +[MissingEnforcePermissionAnnotation] + public void testMethod() {} + ~~~~~~~~~~ +1 errors, 0 warnings""".addLineContinuation()) + } + + fun testDetectIssuesExtraAnnotationInterface() { + lint().files(java( + """ + package test.pkg; + @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) + public class TestClass8 extends IBar.Stub { + public void testMethod() {} + } + """).indented(), + *stubs + ) + .run() + .expect("""src/test/pkg/TestClass8.java:2: Error: The class test.pkg.TestClass8 \ +extends the class IBar.Stub which is not annotated with @EnforcePermission. The same annotation \ +must be used on IBar.Stub. Did you forget to annotate the AIDL definition? \ +[MissingEnforcePermissionAnnotation] +@android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) +^ +1 errors, 0 warnings""".addLineContinuation()) + } + /* Stubs */ + // A service with permission annotation on the class. private val interfaceIFooStub: TestFile = java( """ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE) public interface IFoo { @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE) - public static abstract class Stub implements IFoo { + public static abstract class Stub extends android.os.Binder implements IFoo { @Override public void testMethod() {} } @@ -163,10 +206,11 @@ annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnota """ ).indented() + // A service with permission annotation on the method. private val interfaceIFooMethodStub: TestFile = java( """ public interface IFooMethod { - public static abstract class Stub implements IFooMethod { + public static abstract class Stub extends android.os.Binder implements IFooMethod { @Override @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE) public void testMethod() {} @@ -177,6 +221,19 @@ annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnota """ ).indented() + // A service without any permission annotation. + private val interfaceIBarStub: TestFile = java( + """ + public interface IBar { + public static abstract class Stub extends android.os.Binder implements IBar { + @Override + public void testMethod() {} + } + public void testMethod(); + } + """ + ).indented() + private val manifestPermissionStub: TestFile = java( """ package android.Manifest; @@ -194,7 +251,7 @@ annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnota """ ).indented() - private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub, + private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub, interfaceIBarStub, manifestPermissionStub, enforcePermissionAnnotationStub) // Substitutes "backslash + new line" with an empty string to imitate line continuation diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt new file mode 100644 index 000000000000..05c7850c44c2 --- /dev/null +++ b/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2022 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.google.android.lint.parcel + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.android.tools.lint.checks.infrastructure.TestMode +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue + +@Suppress("UnstableApiUsage") +class SaferParcelCheckerTest : LintDetectorTest() { + override fun getDetector(): Detector = SaferParcelChecker() + + override fun getIssues(): List<Issue> = listOf( + SaferParcelChecker.ISSUE_UNSAFE_API_USAGE + ) + + override fun lint(): TestLintTask = + super.lint() + .allowMissingSdk(true) + // We don't do partial analysis in the platform + .skipTestModes(TestMode.PARTIAL) + + fun testDetectUnsafeReadSerializable() { + lint() + .files( + java( + """ + package test.pkg; + import android.os.Parcel; + import java.io.Serializable; + + public class TestClass { + private TestClass(Parcel p) { + Serializable ans = p.readSerializable(); + } + } + """ + ).indented(), + *includes + ) + .expectIdenticalTestModeOutput(false) + .run() + .expect( + """ + src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readSerializable() \ + API usage [UnsafeParcelApi] + Serializable ans = p.readSerializable(); + ~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """.addLineContinuation() + ) + } + + fun testDoesNotDetectSafeReadSerializable() { + lint() + .files( + java( + """ + package test.pkg; + import android.os.Parcel; + import java.io.Serializable; + + public class TestClass { + private TestClass(Parcel p) { + String ans = p.readSerializable(null, String.class); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect("No warnings.") + } + + fun testDetectUnsafeReadArrayList() { + lint() + .files( + java( + """ + package test.pkg; + import android.os.Parcel; + + public class TestClass { + private TestClass(Parcel p) { + ArrayList ans = p.readArrayList(null); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect( + """ + src/test/pkg/TestClass.java:6: Warning: Unsafe Parcel.readArrayList() API \ + usage [UnsafeParcelApi] + ArrayList ans = p.readArrayList(null); + ~~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """.addLineContinuation() + ) + } + + fun testDoesNotDetectSafeReadArrayList() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + + public class TestClass { + private TestClass(Parcel p) { + ArrayList<Intent> ans = p.readArrayList(null, Intent.class); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect("No warnings.") + } + + fun testDetectUnsafeReadList() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + import java.util.List; + + public class TestClass { + private TestClass(Parcel p) { + List<Intent> list = new ArrayList<Intent>(); + p.readList(list, null); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect( + """ + src/test/pkg/TestClass.java:9: Warning: Unsafe Parcel.readList() API usage \ + [UnsafeParcelApi] + p.readList(list, null); + ~~~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """.addLineContinuation() + ) + } + + fun testDoesNotDetectSafeReadList() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + import java.util.List; + + public class TestClass { + private TestClass(Parcel p) { + List<Intent> list = new ArrayList<Intent>(); + p.readList(list, null, Intent.class); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect("No warnings.") + } + + fun testDetectUnsafeReadParcelable() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + + public class TestClass { + private TestClass(Parcel p) { + Intent ans = p.readParcelable(null); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect( + """ + src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readParcelable() API \ + usage [UnsafeParcelApi] + Intent ans = p.readParcelable(null); + ~~~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """.addLineContinuation() + ) + } + + fun testDoesNotDetectSafeReadParcelable() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + + public class TestClass { + private TestClass(Parcel p) { + Intent ans = p.readParcelable(null, Intent.class); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect("No warnings.") + } + + fun testDetectUnsafeReadParcelableList() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + import java.util.List; + + public class TestClass { + private TestClass(Parcel p) { + List<Intent> list = new ArrayList<Intent>(); + List<Intent> ans = p.readParcelableList(list, null); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect( + """ + src/test/pkg/TestClass.java:9: Warning: Unsafe Parcel.readParcelableList() \ + API usage [UnsafeParcelApi] + List<Intent> ans = p.readParcelableList(list, null); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """.addLineContinuation() + ) + } + + fun testDoesNotDetectSafeReadParcelableList() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + import java.util.List; + + public class TestClass { + private TestClass(Parcel p) { + List<Intent> list = new ArrayList<Intent>(); + List<Intent> ans = + p.readParcelableList(list, null, Intent.class); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect("No warnings.") + } + + fun testDetectUnsafeReadSparseArray() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + import android.util.SparseArray; + + public class TestClass { + private TestClass(Parcel p) { + SparseArray<Intent> ans = p.readSparseArray(null); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect( + """ + src/test/pkg/TestClass.java:8: Warning: Unsafe Parcel.readSparseArray() API\ + usage [UnsafeParcelApi] + SparseArray<Intent> ans = p.readSparseArray(null); + ~~~~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """.addLineContinuation() + ) + } + + fun testDoesNotDetectSafeReadSparseArray() { + lint() + .files( + java( + """ + package test.pkg; + import android.content.Intent; + import android.os.Parcel; + import android.util.SparseArray; + + public class TestClass { + private TestClass(Parcel p) { + SparseArray<Intent> ans = + p.readSparseArray(null, Intent.class); + } + } + """ + ).indented(), + *includes + ) + .run() + .expect("No warnings.") + } + + /** Stubs for classes used for testing */ + + + private val includes = + arrayOf( + manifest().minSdk("Tiramisu"), + java( + """ + package android.os; + import java.util.ArrayList; + import java.util.List; + import java.util.Map; + import java.util.HashMap; + + public final class Parcel { + // Deprecateds + public Object[] readArray(ClassLoader loader) { return null; } + public ArrayList readArrayList(ClassLoader loader) { return null; } + public HashMap readHashMap(ClassLoader loader) { return null; } + public void readList(List outVal, ClassLoader loader) {} + public void readMap(Map outVal, ClassLoader loader) {} + public <T extends Parcelable> T readParcelable(ClassLoader loader) { return null; } + public Parcelable[] readParcelableArray(ClassLoader loader) { return null; } + public Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) { return null; } + public <T extends Parcelable> List<T> readParcelableList(List<T> list, ClassLoader cl) { return null; } + public Serializable readSerializable() { return null; } + public <T> SparseArray<T> readSparseArray(ClassLoader loader) { return null; } + + // Replacements + public <T> T[] readArray(ClassLoader loader, Class<T> clazz) { return null; } + public <T> ArrayList<T> readArrayList(ClassLoader loader, Class<? extends T> clazz) { return null; } + public <K, V> HashMap<K,V> readHashMap(ClassLoader loader, Class<? extends K> clazzKey, Class<? extends V> clazzValue) { return null; } + public <T> void readList(List<? super T> outVal, ClassLoader loader, Class<T> clazz) {} + public <K, V> void readMap(Map<? super K, ? super V> outVal, ClassLoader loader, Class<K> clazzKey, Class<V> clazzValue) {} + public <T> T readParcelable(ClassLoader loader, Class<T> clazz) { return null; } + public <T> T[] readParcelableArray(ClassLoader loader, Class<T> clazz) { return null; } + public <T> Parcelable.Creator<T> readParcelableCreator(ClassLoader loader, Class<T> clazz) { return null; } + public <T> List<T> readParcelableList(List<T> list, ClassLoader cl, Class<T> clazz) { return null; } + public <T> T readSerializable(ClassLoader loader, Class<T> clazz) { return null; } + public <T> SparseArray<T> readSparseArray(ClassLoader loader, Class<? extends T> clazz) { return null; } + } + """ + ).indented(), + java( + """ + package android.os; + public interface Parcelable {} + """ + ).indented(), + java( + """ + package android.content; + public class Intent implements Parcelable, Cloneable {} + """ + ).indented(), + java( + """ + package android.util; + public class SparseArray<E> implements Cloneable {} + """ + ).indented(), + ) + + // Substitutes "backslash + new line" with an empty string to imitate line continuation + private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "") +} diff --git a/tools/preload-check/device/src/com/android/preload/check/Util.java b/tools/preload-check/device/src/com/android/preload/check/Util.java index fccea0a0c107..f521c95839f1 100644 --- a/tools/preload-check/device/src/com/android/preload/check/Util.java +++ b/tools/preload-check/device/src/com/android/preload/check/Util.java @@ -25,8 +25,8 @@ import java.io.FileOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.List; public class Util { @@ -48,7 +48,7 @@ public class Util { Class<?> vmClassLoaderClass = Class.forName("java.lang.VMClassLoader"); Method getResources = vmClassLoaderClass.getDeclaredMethod("getResources", String.class); getResources.setAccessible(true); - LinkedList<DexFile> res = new LinkedList<>(); + ArrayList<DexFile> res = new ArrayList<>(); for (int i = 1;; i++) { try { String name = "classes" + (i > 1 ? String.valueOf(i) : "") + ".dex"; |